feat: add Go migration compatibility foundation

This commit is contained in:
zn-admin committed 2026-08-13 10:35:52 +08:00
1 parent 7de3300034
commit 716a8031b1
27 files changed
+2582

No files matched your search

@@ -0,0 +1,79 @@
// Package application composes the Go backend foundation Modules.
package application
import (
"context"
"net/http"
"os"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/httpapi"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/postgres"
)
type Options struct {
Context context.Context
Getenv postgres.Getenv
ReadFile postgres.ReadFile
}
type App struct {
handler http.Handler
db *postgres.Module
}
func New(options Options) (*App, error) {
ctx := options.Context
if ctx == nil {
ctx = context.Background()
}
getenv := options.Getenv
if getenv == nil {
getenv = os.Getenv
}
readFile := options.ReadFile
if readFile == nil {
readFile = os.ReadFile
}
config, err := postgres.ParseConfig(getenv, readFile)
if err != nil {
return nil, err
}
database, err := postgres.Open(ctx, config)
if err != nil {
return nil, err
}
readiness := databaseReadiness{config: config, store: database.Store}
return &App{
db: database,
handler: httpapi.NewHandler(readiness),
}, nil
}
func (app *App) Handler() http.Handler {
return app.handler
}
func (app *App) Close() {
if app.db != nil {
app.db.Close()
}
}
type databaseReadiness struct {
config postgres.Config
store *postgres.Store
}
func (adapter databaseReadiness) Status() httpapi.DatabaseStatus {
configured := adapter.config.Backend == postgres.BackendLocal || adapter.config.DatabaseURL != ""
return httpapi.DatabaseStatus{
Backend: string(adapter.config.Backend),
Configured: configured,
}
}
func (adapter databaseReadiness) Ready(ctx context.Context) (httpapi.DatabaseStatus, error) {
status := adapter.Status()
return status, adapter.store.Readiness(ctx)
}
@@ -0,0 +1,68 @@
package application_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/application"
)
func TestLocalApplicationServesFoundationHealthAndReadiness(t *testing.T) {
app, err := application.New(application.Options{
Getenv: func(name string) string {
if name == "ZHINIAN_DATA_BACKEND" {
return "local"
}
return ""
},
})
if err != nil {
t.Fatalf("New() error = %v", err)
}
t.Cleanup(app.Close)
for _, test := range []struct {
path string
wantCode int
}{
{path: "/api/health", wantCode: http.StatusOK},
{path: "/api/ready", wantCode: http.StatusOK},
} {
t.Run(test.path, func(t *testing.T) {
response := httptest.NewRecorder()
app.Handler().ServeHTTP(response, httptest.NewRequest(http.MethodGet, test.path, nil))
if response.Code != test.wantCode {
t.Fatalf("status = %d, want %d", response.Code, test.wantCode)
}
var payload struct {
OK bool `json:"ok"`
Database struct {
Backend string `json:"backend"`
Configured bool `json:"configured"`
} `json:"database"`
}
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if !payload.OK || payload.Database.Backend != "local" || !payload.Database.Configured {
t.Fatalf("payload = %+v", payload)
}
})
}
}
func TestApplicationRejectsInvalidProductionDatabaseConfiguration(t *testing.T) {
_, err := application.New(application.Options{
Getenv: func(name string) string {
if name == "NODE_ENV" {
return "production"
}
return ""
},
})
if err == nil {
t.Fatal("New() error = nil, want fail-closed database configuration error")
}
}