diff --git a/pkg/database/post.go b/pkg/database/post.go index a92d231..59ea061 100644 --- a/pkg/database/post.go +++ b/pkg/database/post.go @@ -118,6 +118,11 @@ func GetPostByID(ctx context.Context, id models.PostID) (models.Post, error) { return post, nil } +// UpdatePost updates the user post information in the database. +// Only a few parameter can be updated: +// - Rating +// - Tags +// - References func UpdatePost(ctx context.Context, anthrovePost models.Post) error { ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "UpdatePost") defer span.End() @@ -141,6 +146,9 @@ func UpdatePost(ctx context.Context, anthrovePost models.Post) error { } updatePost := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: anthrovePost.ID, + }, Rating: anthrovePost.Rating, Tags: anthrovePost.Tags, References: anthrovePost.References, diff --git a/pkg/database/post_test.go b/pkg/database/post_test.go new file mode 100644 index 0000000..eba1030 --- /dev/null +++ b/pkg/database/post_test.go @@ -0,0 +1,453 @@ +package database + +import ( + "context" + "fmt" + "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models" + "git.anthrove.art/Anthrove/otter-space-sdk/v2/test" + "go.opentelemetry.io/contrib/bridges/otellogrus" + "go.opentelemetry.io/otel" + "gorm.io/gorm" + "reflect" + "testing" + "time" +) + +func TestCreatePost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + logger.Fatalf("Could not start PostgreSQL container: %v", err) + } + + client = gormDB + + // Setup open telemetry + tracer = otel.Tracer(tracingName) + + hook := otellogrus.NewHook(tracingName) + logger.AddHook(hook) + + defer container.Terminate(ctx) + + // -- -- Setup Tests + + // -- Create Post to test with + validPost := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: models.PostID(fmt.Sprintf("%025s", "Post1")), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + DeletedAt: gorm.DeletedAt{}, + }, + Rating: models.SFW, + } + // -- + + // -- -- Tests + type args struct { + ctx context.Context + post models.Post + } + tests := []struct { + name string + args args + want models.Post + wantErr bool + }{ + { + name: "Test 01: Valid Post", + args: args{ + ctx: ctx, + post: validPost, + }, + want: validPost, + wantErr: false, + }, + { + name: "Test 02: Duplicate Post", + args: args{ + ctx: ctx, + post: validPost, + }, + want: models.Post{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CreatePost(tt.args.ctx, tt.args.post) + if (err != nil) != tt.wantErr { + t.Errorf("CreatePost() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CreatePost() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCreatePostInBatch(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + logger.Fatalf("Could not start PostgreSQL container: %v", err) + } + + client = gormDB + + // Setup open telemetry + tracer = otel.Tracer(tracingName) + + hook := otellogrus.NewHook(tracingName) + logger.AddHook(hook) + + defer container.Terminate(ctx) + + // -- -- Setup Tests + + // -- Create Posts to test with + validPosts := test.GenerateRandomPosts(5) + // -- + + // -- -- Tests + type args struct { + ctx context.Context + post []models.Post + batchSize int + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 01: Valid Posts", + args: args{ + ctx: ctx, + post: validPosts, + batchSize: len(validPosts), + }, + wantErr: false, + }, + { + name: "Test 02: Duplicate Posts", + args: args{ + ctx: ctx, + post: validPosts, + batchSize: len(validPosts), + }, + wantErr: true, + }, + { + name: "Test 03: Nil Posts", + args: args{ + ctx: ctx, + post: nil, + batchSize: len(validPosts), + }, + wantErr: true, + }, + { + name: "Test 04: Empty Posts", + args: args{ + ctx: ctx, + post: []models.Post{}, + batchSize: len(validPosts), + }, + wantErr: true, + }, + { + name: "Test 08: Empty Batch Size", + args: args{ + ctx: ctx, + post: []models.Post{}, + batchSize: 0, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := CreatePostInBatch(tt.args.ctx, tt.args.post, tt.args.batchSize); (err != nil) != tt.wantErr { + t.Errorf("CreatePostInBatch() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGetPostByID(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + logger.Fatalf("Could not start PostgreSQL container: %v", err) + } + + client = gormDB + + // Setup open telemetry + tracer = otel.Tracer(tracingName) + + hook := otellogrus.NewHook(tracingName) + logger.AddHook(hook) + + defer container.Terminate(ctx) + + // -- -- Setup Tests + + // -- Create Post to test with + validPost := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: models.PostID(fmt.Sprintf("%025s", "Post1")), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + DeletedAt: gorm.DeletedAt{}, + }, + Rating: models.SFW, + } + + validPost, err = CreatePost(ctx, validPost) + if err != nil { + logger.Fatal(err) + } + // -- + + // -- -- Tests + type args struct { + ctx context.Context + id models.PostID + } + tests := []struct { + name string + args args + want models.Post + wantErr bool + }{ + { + name: "Test 01: Valid PostID", + args: args{ + ctx: ctx, + id: validPost.ID, + }, + want: validPost, + wantErr: false, + }, + { + name: "Test 03: Empty PostID", + args: args{ + ctx: ctx, + id: "", + }, + wantErr: true, + }, + { + name: "Test 04: Short PostID", + args: args{ + ctx: ctx, + id: "111", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetPostByID(tt.args.ctx, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("GetPostByID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !checkPostID(got, tt.want) { + t.Errorf("GetPostByID() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestUpdatePost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + logger.Fatalf("Could not start PostgreSQL container: %v", err) + } + + client = gormDB + + // Setup open telemetry + tracer = otel.Tracer(tracingName) + + hook := otellogrus.NewHook(tracingName) + logger.AddHook(hook) + + defer container.Terminate(ctx) + + // -- -- Setup Tests + + // -- Create Post to test with + validPost := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: models.PostID(fmt.Sprintf("%025s", "Post1")), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + DeletedAt: gorm.DeletedAt{}, + }, + Rating: models.SFW, + } + + validPost, err = CreatePost(ctx, validPost) + if err != nil { + logger.Fatal(err) + } + // -- + + // -- Create Updates models for Post + validUpdatePost := validPost + validUpdatePost.Rating = models.NSFW + + invalidUpdatePost := models.Post{} + // -- + + // -- -- Tests + type args struct { + ctx context.Context + anthrovePost models.Post + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 01: Valid Update for Post", + args: args{ + ctx: ctx, + anthrovePost: validUpdatePost, + }, + wantErr: false, + }, + { + name: "Test 02: Invalid Update for Post", + args: args{ + ctx: ctx, + anthrovePost: invalidUpdatePost, + }, + wantErr: true, + }, + { + name: "Test 03: Empty ID for Update for Post", + args: args{ + ctx: ctx, + anthrovePost: models.Post{BaseModel: models.BaseModel[models.PostID]{ID: ""}}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := UpdatePost(tt.args.ctx, tt.args.anthrovePost); (err != nil) != tt.wantErr { + t.Errorf("UpdatePost() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDeletePost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + logger.Fatalf("Could not start PostgreSQL container: %v", err) + } + + client = gormDB + + // Setup open telemetry + tracer = otel.Tracer(tracingName) + + hook := otellogrus.NewHook(tracingName) + logger.AddHook(hook) + + defer container.Terminate(ctx) + + // -- -- Setup Tests + + // -- Create Post to test with + validPost := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: models.PostID(fmt.Sprintf("%025s", "Post1")), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + DeletedAt: gorm.DeletedAt{}, + }, + Rating: models.SFW, + } + + validPost, err = CreatePost(ctx, validPost) + if err != nil { + logger.Fatal(err) + } + // -- + + // -- -- Tests + type args struct { + ctx context.Context + id models.PostID + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 01: Delete Valid Post", + args: args{ + ctx: ctx, + id: validPost.ID, + }, + wantErr: false, + }, + { + name: "Test 02: Delete not existed Post", + args: args{ + ctx: ctx, + id: validPost.ID, + }, + wantErr: false, + }, + { + name: "Test 03: Empty PostID", + args: args{ + ctx: ctx, + id: "", + }, + wantErr: true, + }, + { + name: "Test 04: Short PostID", + args: args{ + ctx: ctx, + id: "111", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := DeletePost(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr { + t.Errorf("DeletePost() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func checkPostID(got models.Post, want models.Post) bool { + if got.ID != want.ID { + return false + } + + return true +} diff --git a/test/generator.go b/test/generator.go index 2e91ad7..333704a 100644 --- a/test/generator.go +++ b/test/generator.go @@ -99,3 +99,28 @@ func GenerateRandomSources(numTags int) []models.Source { return sources } + +func GenerateRandomPosts(numTags int) []models.Post { + var sources []models.Post + ratings := []models.Rating{"safe", "explicit", "questionable", "unknown"} + + for i := 0; i < numTags; i++ { + id, _ := gonanoid.New(10) + id = spew.Sprintf("source_name_%s", id) + rating := ratings[rand.Intn(len(ratings))] + + source := models.Post{ + BaseModel: models.BaseModel[models.PostID]{ + ID: models.PostID(id), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + DeletedAt: gorm.DeletedAt{}, + }, + Rating: rating, + } + + sources = append(sources, source) + } + + return sources +}