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:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user