test(project): achieve ≥70% package coverage across all internal packages

- store/sqlite: 75.4% (repos + db coverage)
- host/sub2api: 80.8% (httptest mock server, pure function tests)
- app: 74.2% (handler error paths, NewActionSet closures)
- pack: 72.4%
- provision: 75.2%
- access: 77.3%
- config: 94.7% (lookup mock tests)

All tests pass: build, vet, race, coverage gates.
This commit is contained in:
phamnazage-jpg
2026-05-15 19:26:25 +08:00
parent 70ec9d393b
commit 71cbaf5fa6
74 changed files with 10229 additions and 84 deletions

View File

@@ -193,6 +193,89 @@ func TestSub2APIHostAdapterGetAccountModelsParsesEnvelope(t *testing.T) {
}
}
func TestSub2APIHostAdapterChecksGatewayAccess(t *testing.T) {
server := newSub2APIStubServer(t, sub2APIStubConfig{
requireAPIKey: true,
version: "0.1.126",
})
defer server.Close()
client, err := sub2api.NewClient(server.URL)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
result, err := client.CheckGatewayAccess(context.Background(), sub2api.GatewayAccessCheckRequest{
APIKey: "user-api-key",
ExpectedModel: "deepseek-chat",
})
if err != nil {
t.Fatalf("CheckGatewayAccess() error = %v", err)
}
if !result.OK || !result.HasExpectedModel {
t.Fatalf("CheckGatewayAccess() = %+v, want ok with expected model", result)
}
}
func TestSub2APIHostAdapterDeletesManagedResources(t *testing.T) {
server := newSub2APIStubServer(t, sub2APIStubConfig{
requireAPIKey: true,
version: "0.1.126",
})
defer server.Close()
client, err := sub2api.NewClient(server.URL, sub2api.WithAPIKey("api-key"))
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
if err := client.DeleteAccount(context.Background(), "account_1"); err != nil {
t.Fatalf("DeleteAccount() error = %v", err)
}
if err := client.DeleteChannel(context.Background(), "channel_1"); err != nil {
t.Fatalf("DeleteChannel() error = %v", err)
}
if err := client.DeleteGroup(context.Background(), "group_1"); err != nil {
t.Fatalf("DeleteGroup() error = %v", err)
}
}
func TestSub2APIHostAdapterListManagedResources(t *testing.T) {
server := newSub2APIStubServer(t, sub2APIStubConfig{
requireAPIKey: true,
version: "0.1.126",
})
defer server.Close()
client, err := sub2api.NewClient(server.URL, sub2api.WithAPIKey("api-key"))
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
snapshot, err := client.ListManagedResources(context.Background(), sub2api.ListManagedResourcesRequest{
GroupName: "crm-deepseek-group",
ChannelName: "crm-deepseek-channel",
PlanName: "crm-deepseek-plan",
AccountNamePrefix: "deepseek-",
})
if err != nil {
t.Fatalf("ListManagedResources() error = %v", err)
}
if len(snapshot.Groups) != 1 || snapshot.Groups[0].ID != "group_1" {
t.Fatalf("Groups = %+v, want one group_1 match", snapshot.Groups)
}
if len(snapshot.Channels) != 2 || snapshot.Channels[0].ID != "channel_1" || snapshot.Channels[1].ID != "channel_2" {
t.Fatalf("Channels = %+v, want two matching channels", snapshot.Channels)
}
if len(snapshot.Plans) != 1 || snapshot.Plans[0].ID != "plan_1" {
t.Fatalf("Plans = %+v, want one plan_1 match", snapshot.Plans)
}
if len(snapshot.Accounts) != 2 || snapshot.Accounts[0].ID != "account_1" || snapshot.Accounts[1].ID != "account_2" {
t.Fatalf("Accounts = %+v, want two deepseek account matches", snapshot.Accounts)
}
}
type sub2APIStubConfig struct {
requireAPIKey bool
version string
@@ -220,52 +303,106 @@ func newSub2APIStubServer(t *testing.T, cfg sub2APIStubConfig) *httptest.Server
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodPost {
switch r.Method {
case http.MethodGet:
writeJSON(t, w, http.StatusOK, map[string]any{
"data": []map[string]any{
{"id": "group_1", "name": "crm-deepseek-group"},
{"id": "group_2", "name": "other-group"},
},
})
case http.MethodPost:
var payload map[string]any
_ = json.NewDecoder(r.Body).Decode(&payload)
if payload["name"] == "" || payload["rate_multiplier"] == nil {
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
return
}
writeJSON(t, w, http.StatusCreated, map[string]any{
"data": map[string]any{
"id": "group_1",
"name": payload["name"],
},
})
default:
http.NotFound(w, r)
}
})
mux.HandleFunc("/api/v1/admin/groups/", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodDelete {
http.NotFound(w, r)
return
}
var payload map[string]any
_ = json.NewDecoder(r.Body).Decode(&payload)
if payload["name"] == "" || payload["rate_multiplier"] == nil {
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
return
}
writeJSON(t, w, http.StatusCreated, map[string]any{
"data": map[string]any{
"id": "group_1",
"name": payload["name"],
},
})
w.WriteHeader(http.StatusNoContent)
})
mux.HandleFunc("/api/v1/admin/channels", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodPost {
switch r.Method {
case http.MethodGet:
writeJSON(t, w, http.StatusOK, map[string]any{
"data": []map[string]any{
{"id": "channel_1", "name": "crm-deepseek-channel"},
{"id": "channel_2", "name": "crm-deepseek-channel"},
{"id": "channel_3", "name": "other-channel"},
},
})
case http.MethodPost:
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
default:
http.NotFound(w, r)
}
})
mux.HandleFunc("/api/v1/admin/channels/", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodDelete {
http.NotFound(w, r)
return
}
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
w.WriteHeader(http.StatusNoContent)
})
mux.HandleFunc("/api/v1/admin/payment/plans", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodPost {
switch r.Method {
case http.MethodGet:
writeJSON(t, w, http.StatusOK, map[string]any{
"data": []map[string]any{
{"id": "plan_1", "name": "crm-deepseek-plan"},
{"id": "plan_2", "name": "other-plan"},
},
})
case http.MethodPost:
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
default:
http.NotFound(w, r)
return
}
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
})
mux.HandleFunc("/api/v1/admin/accounts", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
return
}
if r.Method != http.MethodPost {
switch r.Method {
case http.MethodGet:
writeJSON(t, w, http.StatusOK, map[string]any{
"data": []map[string]any{
{"id": "account_1", "name": "deepseek-01"},
{"id": "account_2", "name": "deepseek-02"},
{"id": "account_3", "name": "other-01"},
},
})
case http.MethodPost:
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
default:
http.NotFound(w, r)
return
}
writeJSON(t, w, http.StatusUnprocessableEntity, map[string]any{"error": "validation failed"})
})
mux.HandleFunc("/api/v1/admin/accounts/batch", func(w http.ResponseWriter, r *http.Request) {
if !mustStubAuth(t, w, r, cfg.requireAPIKey) {
@@ -287,6 +424,14 @@ func newSub2APIStubServer(t *testing.T, cfg sub2APIStubConfig) *httptest.Server
return
}
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/v1/admin/accounts/"), "/")
if len(parts) == 1 {
if r.Method != http.MethodDelete {
http.NotFound(w, r)
return
}
w.WriteHeader(http.StatusNoContent)
return
}
if len(parts) != 2 {
http.NotFound(w, r)
return
@@ -333,6 +478,19 @@ func newSub2APIStubServer(t *testing.T, cfg sub2APIStubConfig) *httptest.Server
},
})
})
mux.HandleFunc("/v1/models", func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("x-api-key"); got != "user-api-key" {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"error":"unauthorized"}`))
return
}
writeJSON(t, w, http.StatusOK, map[string]any{
"data": []map[string]any{
{"id": "deepseek-chat"},
{"id": "deepseek-reasoner"},
},
})
})
return httptest.NewServer(mux)
}