Files
ai_site/platform/internal/meta/postgres_test.go
2026-07-31 10:31:17 +08:00

127 lines
3.4 KiB
Go

package meta_test
import (
"context"
"database/sql"
"os"
"testing"
"time"
"aijianzhan/platform/internal/blueprint"
"aijianzhan/platform/internal/meta"
_ "github.com/lib/pq"
)
func testDSN(t *testing.T) string {
t.Helper()
dsn := os.Getenv("PLATFORM_TEST_DSN")
if dsn == "" {
dsn = "postgres://platform:platform@127.0.0.1:5432/platform?sslmode=disable"
}
return dsn
}
func TestPostgresMetaPersist(t *testing.T) {
db, err := sql.Open("postgres", testDSN(t))
if err != nil {
t.Skip(err)
}
if err := db.Ping(); err != nil {
t.Skip(err)
}
defer db.Close()
ctx := context.Background()
if err := meta.EnsureSchema(ctx, db); err != nil {
t.Fatal(err)
}
store := meta.NewPostgresStore(db)
slug := "meta_persist_demo"
tenantID := int64(99001)
_, _ = db.ExecContext(ctx, `DELETE FROM platform_meta.tenant_apps WHERE tenant_id=$1 AND slug=$2`, tenantID, slug)
n := false
bp := &blueprint.Blueprint{
Version: "1.0",
Meta: blueprint.Meta{Name: "持久化测试", Slug: slug, Locale: "zh-CN"},
Storage: blueprint.Storage{Mode: "schema_per_app", Engine: "postgres", SchemaName: "app_t99001_meta_persist_demo"},
Entities: []blueprint.Entity{{
Name: "item", Table: "item", Label: "Item", PrimaryKey: "id",
Fields: []blueprint.Field{
{Name: "id", Type: "bigint", Label: "ID", Nullable: &n},
{Name: "title", Type: "string", Label: "标题", Nullable: &n, MaxLength: 64},
},
}},
Apis: blueprint.Apis{
BasePath: "/api/v1/apps/" + slug,
Resources: []blueprint.APIResource{{
Entity: "item", Path: "/items",
Operations: []string{"list", "get", "create"},
}},
},
Pages: []blueprint.Page{{
ID: "list", Title: "列表", Route: "/items", Type: "list", Entity: "item",
}},
Security: blueprint.Security{
Visibility: "private",
Roles: []blueprint.Role{{
Name: "管理员", Permissions: []string{"读取模块", "写入模块", "发布模块", "新增数据", "查询数据"},
}},
RowPolicies: []blueprint.RowPolicy{{Entity: "item", Rule: "tenant_isolated"}},
},
}
rec := &meta.AppRecord{
AppID: "11111111-1111-1111-1111-111111111111",
TenantID: tenantID,
Slug: slug,
Name: bp.Meta.Name,
SchemaName: bp.Storage.SchemaName,
Engine: "postgres",
Status: meta.StatusPublished,
Blueprint: bp,
DDL: []string{"SELECT 1"},
Endpoints: []string{"GET /api/v1/apps/" + slug + "/items"},
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
if err := store.Save(ctx, rec); err != nil {
t.Fatal(err)
}
got, err := store.GetBySlug(ctx, tenantID, slug)
if err != nil {
t.Fatal(err)
}
if got.Status != meta.StatusPublished || got.Blueprint == nil || got.Blueprint.Meta.Name != "持久化测试" {
t.Fatalf("unexpected record: %+v", got)
}
ref, err := store.ResolveResource(ctx, tenantID, slug, "items")
if err != nil {
t.Fatal(err)
}
if ref.Entity.Name != "item" {
t.Fatalf("entity=%s", ref.Entity.Name)
}
// 二次 Save 应保留 app_id
rec.Name = "持久化测试-更新"
rec.AppID = "22222222-2222-2222-2222-222222222222"
if err := store.Save(ctx, rec); err != nil {
t.Fatal(err)
}
got2, err := store.GetBySlug(ctx, tenantID, slug)
if err != nil {
t.Fatal(err)
}
if got2.AppID != "11111111-1111-1111-1111-111111111111" {
t.Fatalf("app_id should be preserved, got %s", got2.AppID)
}
if got2.Name != "持久化测试-更新" {
t.Fatalf("name not updated: %s", got2.Name)
}
}