TestModelsAPIPostgres exercises the real query path against the config store: sidebar counts, a config-schema listing, a runtime-schema listing (schema qualification), and the end-to-end redaction guarantee — a seeded password hash must never appear in the API response bytes.
(t *testing.T)
| 78 | // qualification), and the end-to-end redaction guarantee — a seeded password |
| 79 | // hash must never appear in the API response bytes. |
| 80 | func TestModelsAPIPostgres(t *testing.T) { |
| 81 | store := newPostgresConfigStore(t) |
| 82 | |
| 83 | const secretHash = "$2a$10$DEADBEEFdeadbeefDEADBEEFdeadbeefDEADBEEFdeadbeefDEADBE" |
| 84 | if err := store.DB().Create(&configstore.Org{Name: "acme", DatabaseName: "acme_db"}).Error; err != nil { |
| 85 | t.Fatalf("seed org: %v", err) |
| 86 | } |
| 87 | if err := store.DB().Create(&configstore.OrgUser{OrgID: "acme", Username: "reader", Password: secretHash}).Error; err != nil { |
| 88 | t.Fatalf("seed user: %v", err) |
| 89 | } |
| 90 | |
| 91 | gin.SetMode(gin.TestMode) |
| 92 | r := gin.New() |
| 93 | registerModelsAPI(r.Group("/api/v1"), store) |
| 94 | |
| 95 | get := func(path string) (*httptest.ResponseRecorder, map[string]json.RawMessage) { |
| 96 | req := httptest.NewRequest(http.MethodGet, path, nil) |
| 97 | rec := httptest.NewRecorder() |
| 98 | r.ServeHTTP(rec, req) |
| 99 | var body map[string]json.RawMessage |
| 100 | if rec.Code == http.StatusOK { |
| 101 | if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { |
| 102 | t.Fatalf("decode %s: %v (%s)", path, err, rec.Body.String()) |
| 103 | } |
| 104 | } |
| 105 | return rec, body |
| 106 | } |
| 107 | |
| 108 | // Sidebar listing. |
| 109 | rec, body := get("/api/v1/models") |
| 110 | if rec.Code != http.StatusOK { |
| 111 | t.Fatalf("GET /models = %d", rec.Code) |
| 112 | } |
| 113 | var summaries []modelSummary |
| 114 | if err := json.Unmarshal(body["models"], &summaries); err != nil { |
| 115 | t.Fatalf("decode summaries: %v", err) |
| 116 | } |
| 117 | gotOrgUsers := false |
| 118 | for _, s := range summaries { |
| 119 | if s.Key == "org-users" { |
| 120 | gotOrgUsers = true |
| 121 | if s.Count != 1 { |
| 122 | t.Errorf("org-users count = %d, want 1", s.Count) |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | if !gotOrgUsers { |
| 127 | t.Fatalf("sidebar missing org-users") |
| 128 | } |
| 129 | |
| 130 | // Config-schema listing must not leak the password hash. |
| 131 | rec, _ = get("/api/v1/models/org-users") |
| 132 | if rec.Code != http.StatusOK { |
| 133 | t.Fatalf("GET /models/org-users = %d", rec.Code) |
| 134 | } |
| 135 | if strings.Contains(rec.Body.String(), secretHash) { |
| 136 | t.Fatalf("org-users listing leaked password hash:\n%s", rec.Body.String()) |
| 137 | } |
nothing calls this directly
no test coverage detected