Files
query-orchestration/internal/uisettings/service_test.go
T
Jacob Mathison 8a4029058b Merged in jmathison/demo-table (pull request #213)
Add demo dashboard settings table and CRUD API

* Add demo dashboard settings table and CRUD API

- Migration 00000000000123: create demoDashboardSettings table (id, guid, key, value JSONB, isDeleted, timestamps)
- SQLC queries and repository for create, list, listByGuid, get, update, soft delete
- OpenAPI paths and schemas in queryAPI.yaml; regenerate api.gen.go (embedded spec)
- Handlers in api/queryAPI/demodashboardsettings.go; ConfigProvider GetDBQueries for repo access
- Unit tests for demo dashboard settings CRUD

* Fix sqlc-generated demo dashboard setting types

Align demo dashboard settings handlers with sqlc output (DemoDashboardSetting model + pointer slices) so CI codegen/build succeeds.

* Fix CI gosec and gofmt issues

- Suppress gosec G115 for TotalCount len->int32 cast (consistent with other list endpoints)
- gofmt mock GetDBQueries() in test helpers

* gofmt query API demo settings

* Tighten demo dashboard settings OpenAPI constraints

* Regenerate query API embedded spec

* refactor: generic UI settings (table, API, service) and three-layer architecture

- Replace demo-dashboard-settings with generic UI settings:
  - Table uiSettings with namespace (was guid), key, value JSONB; UNIQUE(namespace, key)
  - Migration 123: create_ui_settings (single migration, no rename)
  - OpenAPI: /ui-settings, UISettingsService, UISetting schemas with namespace
  - API types and handlers: CreateUISetting, ListUISettings, GetUISetting, UpdateUISetting, DeleteUISetting

- Add service layer (architecture fix):
  - internal/uisettings: Service with Create, List, ListByNamespace, Get, Update, SoftDelete
  - Controllers call s.svc.UISettings only; remove GetDBQueries from queryAPI ConfigProvider
  - Wire UISettings in Services, main.go, and testutils

- Repository: uisettings.sql + uisettings.sql.go, UISetting model; remove demodashboardsettings
- Controller: uisettings.go (replaces demodashboardsettings.go), ErrNotFound from uisettings package
- Tests: uisettings_test.go with namespace-based list/cre…
* Add UISetting model and generated queries for uiSettings table

- models.go: add UISetting struct (sqlc 1.30.0)
- uisettings.sql.go: generated CRUD for ui_settings feature

Made-with: Cursor

* Use repository.UiSetting to match sqlc-generated type

- service and API use repository.UiSetting (sqlc struct name for uiSettings table)
- uisettings.sql.go: keep CreateUISetting/GetUISetting etc. from query names, return *UiSetting
- Fixes Docker/CI build undefined repository.UISetting

* Fix import formatting for golangci-lint

* Lint fix

* uisettings list test

* tests fixes

* Merge main: resolve api.gen.go conflicts (UI settings + DeleteDocument)

* Doc updates and elimination of index redundancy


Approved-by: Jay Brown
2026-03-04 23:00:50 +00:00

183 lines
4.3 KiB
Go

package uisettings_test
import (
"encoding/json"
"testing"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"queryorchestration/internal/uisettings"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type testConfig struct {
serviceconfig.BaseConfig
}
func TestCreate(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
row, err := svc.Create(ctx, "ns1", "key1", []byte(`{"a":1}`))
require.NoError(t, err)
require.NotNil(t, row)
assert.Equal(t, "ns1", row.Namespace)
assert.Equal(t, "key1", row.Key)
var val map[string]interface{}
require.NoError(t, json.Unmarshal(row.Value, &val))
assert.Equal(t, float64(1), val["a"])
assert.False(t, row.IsDeleted)
assert.NotEqual(t, uuid.Nil, row.ID)
}
func TestList(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
_, err := svc.Create(ctx, "ns-a", "k1", []byte(`{}`))
require.NoError(t, err)
_, err = svc.Create(ctx, "ns-b", "k2", []byte(`{"b":2}`))
require.NoError(t, err)
rows, err := svc.List(ctx)
require.NoError(t, err)
require.Len(t, rows, 2)
namespaces := map[string]bool{rows[0].Namespace: true, rows[1].Namespace: true}
assert.True(t, namespaces["ns-a"])
assert.True(t, namespaces["ns-b"])
}
func TestListByNamespace(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
_, err := svc.Create(ctx, "scoped", "k1", []byte(`{"x":1}`))
require.NoError(t, err)
_, err = svc.Create(ctx, "other", "k2", []byte(`{"y":2}`))
require.NoError(t, err)
_, err = svc.Create(ctx, "scoped", "k3", []byte(`{"z":3}`))
require.NoError(t, err)
rows, err := svc.ListByNamespace(ctx, "scoped")
require.NoError(t, err)
require.Len(t, rows, 2)
for _, r := range rows {
assert.Equal(t, "scoped", r.Namespace)
}
}
func TestGet(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
created, err := svc.Create(ctx, "get-ns", "key", []byte(`{"v":1}`))
require.NoError(t, err)
row, err := svc.Get(ctx, created.ID)
require.NoError(t, err)
require.NotNil(t, row)
assert.Equal(t, created.ID, row.ID)
assert.Equal(t, "get-ns", row.Namespace)
assert.Equal(t, "key", row.Key)
}
func TestGet_NotFound(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
_, err := svc.Get(ctx, uuid.New())
assert.ErrorIs(t, err, uisettings.ErrNotFound)
}
func TestGet_AfterSoftDeleteReturnsNotFound(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
created, err := svc.Create(ctx, "del-ns", "key", []byte(`{}`))
require.NoError(t, err)
err = svc.SoftDelete(ctx, created.ID)
require.NoError(t, err)
_, err = svc.Get(ctx, created.ID)
assert.ErrorIs(t, err, uisettings.ErrNotFound)
}
func TestUpdate(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
created, err := svc.Create(ctx, "upd-ns", "k", []byte(`{"old":true}`))
require.NoError(t, err)
err = svc.Update(ctx, created.ID, []byte(`{"new":true}`))
require.NoError(t, err)
row, err := svc.Get(ctx, created.ID)
require.NoError(t, err)
var val map[string]interface{}
require.NoError(t, json.Unmarshal(row.Value, &val))
assert.Equal(t, true, val["new"])
}
func TestSoftDelete(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
created, err := svc.Create(ctx, "softdel-ns", "k", []byte(`{}`))
require.NoError(t, err)
err = svc.SoftDelete(ctx, created.ID)
require.NoError(t, err)
rows, err := svc.List(ctx)
require.NoError(t, err)
for _, r := range rows {
assert.NotEqual(t, created.ID, r.ID)
}
}
func TestList_emptyDB(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
rows, err := svc.List(ctx)
require.NoError(t, err)
assert.Empty(t, rows)
}
func TestListByNamespace_emptyResult(t *testing.T) {
ctx := t.Context()
cfg := &testConfig{}
test.CreateDB(t, cfg)
svc := uisettings.New(cfg)
_, err := svc.Create(ctx, "only-ns", "k", []byte(`{}`))
require.NoError(t, err)
rows, err := svc.ListByNamespace(ctx, "other-namespace")
require.NoError(t, err)
assert.Empty(t, rows)
}