115 lines
3.3 KiB
Go
115 lines
3.3 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func TestCreateProjectReturnsConflictForDuplicateUserName(t *testing.T) {
|
|
t.Run("sqlite", func(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "projects.db")), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
closeTestDatabase(t, db)
|
|
testCreateProjectNameConflict(t, db)
|
|
})
|
|
|
|
t.Run("postgres", func(t *testing.T) {
|
|
dsn := os.Getenv("CANVAS_TEST_POSTGRES_DSN")
|
|
if dsn == "" {
|
|
t.Skip("set CANVAS_TEST_POSTGRES_DSN to an isolated test database")
|
|
}
|
|
config := &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}
|
|
admin, err := gorm.Open(postgres.Open(dsn), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
closeTestDatabase(t, admin)
|
|
|
|
schema := fmt.Sprintf("project_name_conflict_%d", time.Now().UnixNano())
|
|
if err := admin.Exec("CREATE SCHEMA " + schema).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := admin.Exec("DROP SCHEMA " + schema + " CASCADE").Error; err != nil {
|
|
t.Errorf("drop test schema: %v", err)
|
|
}
|
|
})
|
|
|
|
parsed, err := url.Parse(dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
query := parsed.Query()
|
|
query.Set("search_path", schema)
|
|
parsed.RawQuery = query.Encode()
|
|
db, err := gorm.Open(postgres.Open(parsed.String()), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
closeTestDatabase(t, db)
|
|
testCreateProjectNameConflict(t, db)
|
|
})
|
|
}
|
|
|
|
// testCreateProjectNameConflict 验证名称冲突按用户区分,且不误判项目 ID 冲突。
|
|
func testCreateProjectNameConflict(t *testing.T, db *gorm.DB) {
|
|
t.Helper()
|
|
if err := db.AutoMigrate(&model.Project{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repo := New(db)
|
|
|
|
first := model.Project{ID: "project-1", UserID: "user-1", Name: "短剧", Description: "原项目内容"}
|
|
if err := repo.CreateProject(&first); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
duplicate := model.Project{ID: "project-2", UserID: "user-1", Name: "短剧", Description: "重复请求的内容"}
|
|
if err := repo.CreateProject(&duplicate); !errors.Is(err, ErrProjectNameConflict) {
|
|
t.Fatalf("duplicate name error = %v, want ErrProjectNameConflict", err)
|
|
}
|
|
stored, err := repo.ProjectForUser(first.UserID, first.ID)
|
|
if err != nil || stored.Description != first.Description {
|
|
t.Fatalf("duplicate creation changed the original project: project=%#v, error=%v", stored, err)
|
|
}
|
|
|
|
sameNameForAnotherUser := model.Project{ID: "project-3", UserID: "user-2", Name: "短剧"}
|
|
if err := repo.CreateProject(&sameNameForAnotherUser); err != nil {
|
|
t.Fatalf("same name for another user: %v", err)
|
|
}
|
|
|
|
primaryKeyConflict := model.Project{ID: "project-1", UserID: "user-3", Name: "其他项目"}
|
|
if err := repo.CreateProject(&primaryKeyConflict); err == nil || errors.Is(err, ErrProjectNameConflict) {
|
|
t.Fatalf("primary key conflict error = %v, want a different database error", err)
|
|
}
|
|
}
|
|
|
|
func closeTestDatabase(t *testing.T, db *gorm.DB) {
|
|
t.Helper()
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := sqlDB.Close(); err != nil {
|
|
t.Errorf("close test database: %v", err)
|
|
}
|
|
})
|
|
}
|