diff --git a/internal/postgres/post_test.go b/internal/postgres/post_test.go index cf2ebc4..70d2b37 100644 --- a/internal/postgres/post_test.go +++ b/internal/postgres/post_test.go @@ -348,8 +348,10 @@ func TestGetPostBySourceID(t *testing.T) { func checkPost(got *models.Post, want *models.Post) bool { - if got == nil { + if got == nil && want == nil { return true + } else if got == nil || want == nil { + return false } if got.ID != want.ID { diff --git a/pkg/database/database.go b/pkg/database/database.go index 6db8e3c..10d203a 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -32,7 +32,7 @@ type OtterSpace interface { CheckReferenceBetweenUserAndPost(ctx context.Context, anthroveUserID models.AnthroveUserID, sourcePostID models.AnthrovePostID) (bool, error) // GetPostByAnthroveID retrieves a post by its Anthrove ID. - GetPostByAnthroveID(ctx context.Context, anthrovePost *models.Post) (*models.Post, error) + GetPostByAnthroveID(ctx context.Context, anthrovePostID models.AnthrovePostID) (*models.Post, error) // GetPostByURL retrieves a post by its source URL. GetPostByURL(ctx context.Context, sourceUrl string) (*models.Post, error) diff --git a/pkg/database/postgres.go b/pkg/database/postgres.go index 44654aa..4245c9b 100644 --- a/pkg/database/postgres.go +++ b/pkg/database/postgres.go @@ -100,8 +100,8 @@ func (p *postgresqlConnection) CheckReferenceBetweenUserAndPost(ctx context.Cont return postgres.CheckReferenceBetweenUserAndPost(ctx, p.db, anthroveUserID, anthrovePostID) } -func (p *postgresqlConnection) GetPostByAnthroveID(ctx context.Context, anthrovePost *models.Post) (*models.Post, error) { - return postgres.GetPostByAnthroveID(ctx, p.db, models.AnthrovePostID(anthrovePost.ID)) +func (p *postgresqlConnection) GetPostByAnthroveID(ctx context.Context, anthrovePostID models.AnthrovePostID) (*models.Post, error) { + return postgres.GetPostByAnthroveID(ctx, p.db, anthrovePostID) } func (p *postgresqlConnection) GetPostByURL(ctx context.Context, sourceUrl string) (*models.Post, error) { diff --git a/pkg/database/postgres_test.go b/pkg/database/postgres_test.go index 0249f43..d14a354 100644 --- a/pkg/database/postgres_test.go +++ b/pkg/database/postgres_test.go @@ -1,6 +1,11 @@ package database import ( + "context" + "fmt" + "git.dragse.it/anthrove/otter-space-sdk/internal/postgres" + "git.dragse.it/anthrove/otter-space-sdk/pkg/models" + "git.dragse.it/anthrove/otter-space-sdk/test" "testing" ) @@ -24,3 +29,764 @@ func TestNewPostgresqlConnection(t *testing.T) { }) } } + +func Test_postgresqlConnection_Connect(t *testing.T) { + + // Setup trow away container + ctx := context.Background() + container, _, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Tests + + dbConfig, err := test.DatabaseModesFromConnectionString(ctx, container) + if err != nil { + t.Fatalf("Could not parse database config: %v", err) + } + + // Test + type args struct { + in0 context.Context + config models.DatabaseConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Connect to postgresql connection", + args: args{ + in0: ctx, + config: *dbConfig, + }, + wantErr: false, + }, + { + name: "Test 1: Empty connection config", + args: args{ + in0: ctx, + config: models.DatabaseConfig{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{} + if err := p.Connect(tt.args.in0, tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("Connect() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreateUserWithRelationToSource(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + + source := &models.Source{ + BaseModel: models.BaseModel{ID: fmt.Sprintf("%025s", "1")}, + DisplayName: "e621", + Domain: "e621.net", + Icon: "icon.e621.net", + } + err = postgres.CreateSource(ctx, gormDB, source) + if err != nil { + t.Fatal(err) + } + + // Test + type args struct { + ctx context.Context + anthroveUserID models.AnthroveUserID + sourceID models.AnthroveSourceID + accountId string + accountUsername string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid anthroveUserID, sourceID, accountId, accountUsername", + args: args{ + ctx: ctx, + anthroveUserID: "1", + sourceID: models.AnthroveSourceID(source.ID), + accountId: "e1", + accountUsername: "marius", + }, + wantErr: false, + }, + { + name: "Test 2: Invalid anthroveUserID, valid sourceID, accountId, accountUsername", + args: args{ + ctx: ctx, + anthroveUserID: "2", + sourceID: models.AnthroveSourceID(source.ID), + accountId: "e1", + accountUsername: "marius", + }, + wantErr: true, + }, + { + name: "Test 3: Empty anthroveUserID", + args: args{ + ctx: ctx, + anthroveUserID: "", + sourceID: models.AnthroveSourceID(source.ID), + accountId: "e1", + accountUsername: "marius", + }, + wantErr: true, + }, + { + name: "Test 4: invalid sourceID", + args: args{ + ctx: ctx, + anthroveUserID: "1", + sourceID: "fa.net", + accountId: "e1", + accountUsername: "marius", + }, + wantErr: true, + }, + { + name: "Test 5: no accountId", + args: args{ + ctx: ctx, + anthroveUserID: "1", + sourceID: models.AnthroveSourceID(source.ID), + accountId: "", + accountUsername: "marius", + }, + wantErr: true, + }, + { + name: "Test 6: no accountUsername", + args: args{ + ctx: ctx, + anthroveUserID: "1", + sourceID: models.AnthroveSourceID(source.ID), + accountId: "aa", + accountUsername: "", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreateUserWithRelationToSource(tt.args.ctx, tt.args.anthroveUserID, tt.args.sourceID, tt.args.accountId, tt.args.accountUsername); (err != nil) != tt.wantErr { + t.Errorf("CreateUserWithRelationToSource() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreateSource(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + + validSource := &models.Source{ + DisplayName: "e621", + Domain: "e621.net", + Icon: "icon.e621.net", + } + + invalidSource := &models.Source{ + Domain: "", + } + + // Test + type args struct { + ctx context.Context + anthroveSource *models.Source + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid anthroveSource", + args: args{ + ctx: ctx, + anthroveSource: validSource, + }, + wantErr: false, + }, + { + name: "Test 2: inValid anthroveSource", + args: args{ + ctx: ctx, + anthroveSource: invalidSource, + }, + wantErr: true, + }, + { + name: "Test 3: unique anthroveSource", + args: args{ + ctx: ctx, + anthroveSource: validSource, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreateSource(tt.args.ctx, tt.args.anthroveSource); (err != nil) != tt.wantErr { + t.Errorf("CreateSource() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreatePost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Tests + + validPost := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + invalidPost := &models.Post{ + Rating: "error", + } + + // Test + type args struct { + ctx context.Context + anthrovePost *models.Post + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid AnthrovePostID and Rating", + args: args{ + ctx: context.Background(), + anthrovePost: validPost, + }, + wantErr: false, + }, + { + name: "Test 2: Invalid Rating", + args: args{ + ctx: context.Background(), + anthrovePost: invalidPost, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreatePost(tt.args.ctx, tt.args.anthrovePost); (err != nil) != tt.wantErr { + t.Errorf("CreatePost() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreateTagAndReferenceToPost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + + post := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + err = postgres.CreatePost(ctx, gormDB, post) + if err != nil { + t.Fatal(err) + } + + tag := &models.Tag{ + Name: "JayTheFerret", + Type: "artist", + } + + // Test + type args struct { + ctx context.Context + anthrovePostID models.AnthrovePostID + anthroveTag *models.Tag + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid PostID and Tag", + args: args{ + ctx: ctx, + anthrovePostID: models.AnthrovePostID(post.ID), + anthroveTag: tag, + }, + wantErr: false, + }, + { + name: "Test 2: Valid PostID and no Tag", + args: args{ + ctx: ctx, + anthrovePostID: models.AnthrovePostID(post.ID), + anthroveTag: nil, + }, + wantErr: true, + }, + { + name: "Test 3: Invalid PostID and valid Tag", + args: args{ + ctx: ctx, + anthrovePostID: "123456", + anthroveTag: tag, + }, + wantErr: true, + }, + { + name: "Test 4: No PostID and valid Tag", + args: args{ + ctx: ctx, + anthrovePostID: "", + anthroveTag: tag, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreateTagAndReferenceToPost(tt.args.ctx, tt.args.anthrovePostID, tt.args.anthroveTag); (err != nil) != tt.wantErr { + t.Errorf("CreateTagAndReferenceToPost() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreateReferenceBetweenPostAndSource(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + post := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + err = postgres.CreatePost(ctx, gormDB, post) + if err != nil { + t.Fatal(err) + } + + source := &models.Source{ + DisplayName: "e621", + Domain: "e621.net", + Icon: "icon.e621.net", + } + err = postgres.CreateSource(ctx, gormDB, source) + if err != nil { + t.Fatal(err) + } + + // Test + type args struct { + ctx context.Context + anthrovePostID models.AnthrovePostID + sourceDomain models.AnthroveSourceDomain + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid AnthrovePostID and anthroveSourceDomain", + args: args{ + ctx: ctx, + anthrovePostID: models.AnthrovePostID(post.ID), + sourceDomain: "e621.net", + }, + wantErr: false, + }, + { + name: "Test 2: Invalid AnthrovePostID and Valid anthroveSourceDomain", + args: args{ + ctx: ctx, + anthrovePostID: "123456", + sourceDomain: "e621.net", + }, + wantErr: true, + }, + { + name: "Test 3: Invalid anthroveSourceDomain and Valid AnthrovePostID", + args: args{ + ctx: ctx, + anthrovePostID: "1234", + sourceDomain: "fa.banana", + }, + wantErr: true, + }, + { + name: "Test 4: Invalid anthroveSourceDomain and Invalid AnthrovePostID", + args: args{ + ctx: ctx, + anthrovePostID: "696969", + sourceDomain: "hehe.funny.number", + }, + wantErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreateReferenceBetweenPostAndSource(tt.args.ctx, tt.args.anthrovePostID, tt.args.sourceDomain); (err != nil) != tt.wantErr { + t.Errorf("CreateReferenceBetweenPostAndSource() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CreateReferenceBetweenUserAndPost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + err = postgres.CreateUser(ctx, gormDB, "1") + if err != nil { + t.Fatal(err) + } + + post := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + err = postgres.CreatePost(ctx, gormDB, post) + if err != nil { + t.Fatal(err) + } + + // Test + type args struct { + ctx context.Context + anthroveUserID models.AnthroveUserID + anthrovePostID models.AnthrovePostID + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Test 1: Valid AnthroveUserID and AnthrovePostID", + args: args{ + ctx: ctx, + anthroveUserID: "1", + anthrovePostID: models.AnthrovePostID(post.ID), + }, + wantErr: false, + }, + { + name: "Test 2: Valid AnthroveUserID and invalid AnthrovePostID", + args: args{ + ctx: ctx, + anthroveUserID: "1", + anthrovePostID: "123456", + }, + wantErr: true, + }, + { + name: "Test 3: Valid AnthrovePostID and invalid AnthroveUserID", + args: args{ + ctx: ctx, + anthroveUserID: "123", + anthrovePostID: "1234", + }, + wantErr: true, + }, + { + name: "Test 4: Invalid AnthrovePostID and invalid AnthroveUserID", + args: args{ + ctx: ctx, + anthroveUserID: "123", + anthrovePostID: "123456", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + if err := p.CreateReferenceBetweenUserAndPost(tt.args.ctx, tt.args.anthroveUserID, tt.args.anthrovePostID); (err != nil) != tt.wantErr { + t.Errorf("CreateReferenceBetweenUserAndPost() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_postgresqlConnection_CheckReferenceBetweenUserAndPost(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Test + err = postgres.CreateUser(ctx, gormDB, "1") + if err != nil { + t.Fatal(err) + } + + post := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + err = postgres.CreatePost(ctx, gormDB, post) + if err != nil { + t.Fatal(err) + } + + err = postgres.CreateReferenceBetweenUserAndPost(ctx, gormDB, "1", models.AnthrovePostID(post.ID)) + if err != nil { + t.Fatal(err) + } + + // Test + type args struct { + ctx context.Context + anthroveUserID models.AnthroveUserID + anthrovePostID models.AnthrovePostID + } + tests := []struct { + name string + args args + want bool + wantErr bool + }{ + { + name: "Test 1: Valid AnthroveUserID and AnthrovePostID", + args: args{ + ctx: ctx, + anthroveUserID: "1", + anthrovePostID: models.AnthrovePostID(post.ID), + }, + want: true, + wantErr: false, + }, + { + name: "Test 2: Valid AnthroveUserID and invalid AnthrovePostID", + args: args{ + ctx: ctx, + anthroveUserID: "1", + anthrovePostID: "qadw", + }, + want: false, + wantErr: false, + }, + { + name: "Test 3: Valid AnthrovePostID and invalid AnthroveUserID", + args: args{ + ctx: ctx, + anthroveUserID: "123", + anthrovePostID: models.AnthrovePostID(post.ID), + }, + want: false, + wantErr: false, + }, + { + name: "Test 4: Invalid AnthrovePostID and invalid AnthroveUserID", + args: args{ + ctx: ctx, + anthroveUserID: "123", + anthrovePostID: "123456", + }, + want: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + got, err := p.CheckReferenceBetweenUserAndPost(tt.args.ctx, tt.args.anthroveUserID, tt.args.anthrovePostID) + if (err != nil) != tt.wantErr { + t.Errorf("CheckReferenceBetweenUserAndPost() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CheckReferenceBetweenUserAndPost() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_postgresqlConnection_GetPostByAnthroveID(t *testing.T) { + // Setup trow away container + ctx := context.Background() + container, gormDB, err := test.StartPostgresContainer(ctx) + if err != nil { + t.Fatalf("Could not start PostgreSQL container: %v", err) + } + defer container.Terminate(ctx) + + // Setup Tests + + post := &models.Post{ + BaseModel: models.BaseModel{ + ID: fmt.Sprintf("%025s", "1"), + }, + Rating: "safe", + } + + err = postgres.CreatePost(ctx, gormDB, post) + if err != nil { + t.Fatal("Could not create post", err) + } + + // Test + type args struct { + ctx context.Context + anthrovePost models.AnthrovePostID + } + tests := []struct { + name string + args args + want *models.Post + wantErr bool + }{ + { + name: "Test 1: Valid anthrovePostID", + args: args{ + ctx: ctx, + anthrovePost: models.AnthrovePostID(post.ID), + }, + want: post, + wantErr: false, + }, + { + name: "Test 2: No anthrovePostID", + args: args{ + ctx: ctx, + anthrovePost: "nil", + }, + want: nil, + wantErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &postgresqlConnection{ + db: gormDB, + debug: true, + } + got, err := p.GetPostByAnthroveID(tt.args.ctx, tt.args.anthrovePost) + if (err != nil) != tt.wantErr { + t.Errorf("GetPostByAnthroveID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !checkPost(got, tt.want) { + t.Errorf("GetPostByAnthroveID() got = %v, want %v", got, tt.want) + } + }) + } +} + +func checkPost(got *models.Post, want *models.Post) bool { + + if got == nil && want == nil { + return true + } else if got == nil || want == nil { + return false + } + + if got.ID != want.ID { + return false + } + + if got.Rating != want.Rating { + return false + } + + return true +} diff --git a/test/helper.go b/test/helper.go index 501ac62..6fed12e 100644 --- a/test/helper.go +++ b/test/helper.go @@ -3,11 +3,15 @@ package test import ( "context" "database/sql" + "git.dragse.it/anthrove/otter-space-sdk/pkg/models" migrate "github.com/rubenv/sql-migrate" postgrescontainer "github.com/testcontainers/testcontainers-go/modules/postgres" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" + "net/url" + "strconv" + "strings" "time" "github.com/testcontainers/testcontainers-go" @@ -78,3 +82,41 @@ func getGormDB(connectionString string) (*gorm.DB, error) { Logger: logger.Default.LogMode(logger.Info), }) } + +func DatabaseModesFromConnectionString(ctx context.Context, pgContainer *postgrescontainer.PostgresContainer) (*models.DatabaseConfig, error) { + var err error + + connectionString, err := pgContainer.ConnectionString(ctx) + if err != nil { + return nil, err + } + + url, err := url.Parse(connectionString) + if err != nil { + return nil, err + } + + split := strings.Split(url.Host, ":") + host := split[0] + + port, err := strconv.Atoi(split[1]) + if err != nil { + return nil, err + } + + database := strings.TrimPrefix(url.Path, "/") + + username := url.User.Username() + password, _ := url.User.Password() + + return &models.DatabaseConfig{ + Endpoint: host, + Username: username, + Password: password, + Database: database, + Port: port, + SSL: false, + Timezone: "Europe/Berlin", + Debug: true, + }, nil +}