// InMemoryStore 的單元測試(DB-on FK 收尾,問題 #1)。 // // 不帶 build tag:屬於預設 `go test ./...` 範圍(無需 Docker)。 // 驗證 Upsert / Get 的核心語意,並與 postgres_store_db_test.go 的 dbtest 對齊行為。 package user import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestInMemoryStore_UpsertAndGet(t *testing.T) { ctx := context.Background() s := NewInMemoryStore() in := &User{ID: "sub-001", Email: "alice@example.com", Name: "Alice", Roles: []string{"admin"}} require.NoError(t, s.Upsert(ctx, in)) got, err := s.Get(ctx, "sub-001") require.NoError(t, err) assert.Equal(t, "sub-001", got.ID) assert.Equal(t, "alice@example.com", got.Email) assert.Equal(t, "Alice", got.Name) assert.Equal(t, []string{"admin"}, got.Roles) assert.False(t, got.CreatedAt.IsZero(), "CreatedAt 應自動填入") assert.False(t, got.UpdatedAt.IsZero(), "UpdatedAt 應自動填入") } func TestInMemoryStore_Upsert_UpdatesAndPreservesCreatedAt(t *testing.T) { ctx := context.Background() s := NewInMemoryStore() require.NoError(t, s.Upsert(ctx, &User{ID: "sub-002", Email: "old@example.com", Name: "Old"})) first, err := s.Get(ctx, "sub-002") require.NoError(t, err) origCreated := first.CreatedAt // 同 ID 再 upsert:更新 email/name,保留 created_at。 require.NoError(t, s.Upsert(ctx, &User{ID: "sub-002", Email: "new@example.com", Name: "New"})) second, err := s.Get(ctx, "sub-002") require.NoError(t, err) assert.Equal(t, "new@example.com", second.Email, "email 應更新") assert.Equal(t, "New", second.Name, "name 應更新") assert.Equal(t, origCreated, second.CreatedAt, "created_at 應保留") } func TestInMemoryStore_Get_NotFound(t *testing.T) { ctx := context.Background() s := NewInMemoryStore() _, err := s.Get(ctx, "nope") assert.ErrorIs(t, err, ErrNotFound) } func TestInMemoryStore_Upsert_RequiresIDAndEmail(t *testing.T) { ctx := context.Background() s := NewInMemoryStore() assert.Error(t, s.Upsert(ctx, &User{ID: "", Email: "x@y.z"}), "缺 ID 應回錯") assert.Error(t, s.Upsert(ctx, &User{ID: "sub", Email: ""}), "缺 email 應回錯(users.email NOT NULL)") assert.Error(t, s.Upsert(ctx, nil), "nil 應回錯") } // TestInMemoryStore_Upsert_RolesCopied 確認 Upsert/Get 對 roles 做 copy, // 外部後續修改傳入 slice 不影響 store 內容(對齊 in-memory copy 語意)。 func TestInMemoryStore_Upsert_RolesCopied(t *testing.T) { ctx := context.Background() s := NewInMemoryStore() roles := []string{"a", "b"} require.NoError(t, s.Upsert(ctx, &User{ID: "sub-003", Email: "c@d.e", Roles: roles})) roles[0] = "MUTATED" // 外部改傳入 slice got, err := s.Get(ctx, "sub-003") require.NoError(t, err) assert.Equal(t, []string{"a", "b"}, got.Roles, "store 內 roles 不應被外部 mutation 影響") }