Expand coverage for runtime and sqlite paths

This commit is contained in:
phamnazage-jpg
2026-05-23 10:55:57 +08:00
parent 2ad277743d
commit bcc67c4a8a
17 changed files with 3393 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -202,6 +202,64 @@ func TestBatchImportHTTP(t *testing.T) {
})
}
func TestBatchImportWrapperFunctions(t *testing.T) {
t.Parallel()
t.Run("handleCreateBatchImportRun requires action", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodPost, "/api/batch-import/runs", map[string]any{}, "")
rec := &responseRecorder{header: map[string][]string{}}
handleCreateBatchImportRun(rec, req, nil)
assertStatusCode(t, rec, http.StatusInternalServerError)
assertJSONContains(t, rec.Body().Bytes(), "error.code", "server_misconfigured")
})
t.Run("handleCreateBatchImportRun classifies action error", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodPost, "/api/batch-import/runs", map[string]any{
"host_id": "host-1",
"mode": "strict",
"access_mode": "self_service",
"probe_api_key": "probe-key",
"entries": []map[string]any{
{"base_url": "https://kimi.example.com/v1", "api_key": "sk-test"},
},
}, "")
rec := &responseRecorder{header: map[string][]string{}}
handleCreateBatchImportRun(rec, req, func(context.Context, CreateBatchImportRunRequest) (BatchImportRunCreateResponse, error) {
return BatchImportRunCreateResponse{}, fmt.Errorf("host x not found")
})
assertStatusCode(t, rec, http.StatusNotFound)
assertJSONContains(t, rec.Body().Bytes(), "error.code", "not_found")
})
t.Run("handleListBatchImportRuns requires action", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodGet, "/api/batch-import/runs", nil, "")
rec := &responseRecorder{header: map[string][]string{}}
handleListBatchImportRuns(rec, req, nil)
assertStatusCode(t, rec, http.StatusInternalServerError)
assertJSONContains(t, rec.Body().Bytes(), "error.code", "server_misconfigured")
})
t.Run("handleListBatchImportRuns returns empty array", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodGet, "/api/batch-import/runs?limit=5", nil, "")
rec := &responseRecorder{header: map[string][]string{}}
handleListBatchImportRuns(rec, req, func(_ context.Context, got ListBatchImportRunsRequest) (ListBatchImportRunsResponse, error) {
if got.Limit != 5 {
t.Fatalf("ListBatchImportRunsRequest.Limit = %d, want 5", got.Limit)
}
return ListBatchImportRunsResponse{}, nil
})
assertStatusCode(t, rec, http.StatusOK)
runs, ok := decodeTopLevelArray(t, rec.Body().Bytes(), "runs")
if !ok || len(runs) != 0 {
t.Fatalf("runs = %#v, want empty array", runs)
}
})
}
func newBatchImportActionStubServer(t *testing.T) http.Handler {
t.Helper()

View File

@@ -173,6 +173,72 @@ func TestBatchRunsHTTP(t *testing.T) {
})
}
func TestBatchRunWrapperFunctions(t *testing.T) {
t.Parallel()
t.Run("handleGetBatchImportRun validates inputs", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodGet, "/api/batch-import/runs/run-1", nil, "")
rec := &responseRecorder{header: map[string][]string{}}
handleGetBatchImportRun(rec, req, nil)
assertStatusCode(t, rec, http.StatusInternalServerError)
req = httptestRequest(t, http.MethodGet, "/api/batch-import/runs/", nil, "")
rec = &responseRecorder{header: map[string][]string{}}
handleGetBatchImportRun(rec, req, func(context.Context, string) (batch.RunSummaryProjection, error) {
return batch.RunSummaryProjection{}, nil
})
assertStatusCode(t, rec, http.StatusBadRequest)
assertJSONContains(t, rec.Body().Bytes(), "error.message", "run_id is required")
})
t.Run("handleListBatchImportRunItems validates run id and empty result", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodGet, "/api/batch-import/runs/run-1/items?has_warning=true", nil, "")
rec := &responseRecorder{header: map[string][]string{}}
handleListBatchImportRunItems(rec, req, nil)
assertStatusCode(t, rec, http.StatusInternalServerError)
req = httptestRequest(t, http.MethodGet, "/api/batch-import/runs//items?has_warning=true", nil, "")
rec = &responseRecorder{header: map[string][]string{}}
handleListBatchImportRunItems(rec, req, func(context.Context, ListBatchImportRunItemsRequest) (ListBatchImportRunItemsResponse, error) {
return ListBatchImportRunItemsResponse{}, nil
})
assertStatusCode(t, rec, http.StatusBadRequest)
req = httptestRequest(t, http.MethodGet, "/api/batch-import/runs/run-1/items?has_warning=true&limit=3", nil, "")
req.SetPathValue("run_id", "run-1")
rec = &responseRecorder{header: map[string][]string{}}
handleListBatchImportRunItems(rec, req, func(_ context.Context, got ListBatchImportRunItemsRequest) (ListBatchImportRunItemsResponse, error) {
if got.RunID != "run-1" || got.Limit != 3 || got.HasWarning == nil || !*got.HasWarning {
t.Fatalf("ListBatchImportRunItemsRequest = %+v, want parsed filters", got)
}
return ListBatchImportRunItemsResponse{}, nil
})
assertStatusCode(t, rec, http.StatusOK)
items, ok := decodeTopLevelArray(t, rec.Body().Bytes(), "items")
if !ok || len(items) != 0 {
t.Fatalf("items = %#v, want empty array", items)
}
})
t.Run("handleGetBatchImportRunItem validates ids", func(t *testing.T) {
t.Parallel()
req := httptestRequest(t, http.MethodGet, "/api/batch-import/runs/run-1/items/item-1", nil, "")
rec := &responseRecorder{header: map[string][]string{}}
handleGetBatchImportRunItem(rec, req, nil)
assertStatusCode(t, rec, http.StatusInternalServerError)
req = httptestRequest(t, http.MethodGet, "/api/batch-import/runs//items/", nil, "")
rec = &responseRecorder{header: map[string][]string{}}
handleGetBatchImportRunItem(rec, req, func(context.Context, GetBatchImportRunItemRequest) (batch.ItemDetailProjection, error) {
return batch.ItemDetailProjection{}, nil
})
assertStatusCode(t, rec, http.StatusBadRequest)
assertJSONContains(t, rec.Body().Bytes(), "error.message", "run_id and item_id are required")
})
}
func batchProbeProfile() probe.CapabilityProfile {
return probe.CapabilityProfile{
TransportProfile: probe.TransportProfile{

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
@@ -467,6 +468,194 @@ func TestReconcileProbeAPIKeyRejectsUnsupportedClosureType(t *testing.T) {
}
}
func TestReconcileProbeAPIKeyRequiresAccessClosure(t *testing.T) {
t.Parallel()
store := openReconcileBackgroundTestStore(t)
defer closeAppTestStore(t, store)
batchID, _, _ := seedReconcileBackgroundBatch(t, store)
hostRow := mustGetBackgroundHost(t, store)
_, err := reconcileProbeAPIKey(context.Background(), store, hostRow, sqlite.ImportBatch{ID: batchID}, nil)
if err == nil || err.Error() != fmt.Sprintf("access closure not found for batch %d", batchID) {
t.Fatalf("reconcileProbeAPIKey() error = %v, want missing access closure", err)
}
}
func TestReconcileProbeAPIKeySubscriptionSuccess(t *testing.T) {
t.Parallel()
var assignCalls int
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && strings.HasPrefix(r.URL.RequestURI(), "/api/v1/admin/users?"):
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"items": []map[string]any{}}})
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/admin/users":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"id": 84, "email": "relay-sub-user-1@sub2api.local"}})
case r.Method == http.MethodPut && r.URL.Path == "/api/v1/admin/users/84":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"id": 84}})
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/admin/users/84/balance":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"id": 84}})
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/admin/subscriptions/assign":
assignCalls++
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"id": 401}})
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/auth/login":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"access_token": "user-jwt"}})
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/keys":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"id": 501, "key": "sk-relay-key", "name": "managed-key"}})
case r.Method == http.MethodPut && r.URL.Path == "/api/v1/admin/api-keys/501":
writeJSON(w, http.StatusOK, map[string]any{"data": map[string]any{"api_key": map[string]any{"id": 501}}})
default:
http.NotFound(w, r)
}
}))
defer server.Close()
store := openReconcileBackgroundTestStore(t)
defer closeAppTestStore(t, store)
hostPK, err := store.Hosts().Create(context.Background(), sqlite.Host{
HostID: "host-subscription",
BaseURL: server.URL,
HostVersion: "0.1.126",
CapabilityProbeJSON: "{}",
AuthType: "bearer",
AuthToken: "admin-token",
})
if err != nil {
t.Fatalf("Hosts().Create() error = %v", err)
}
packPK := createBackgroundPack(t, store)
providerPK := createBackgroundProvider(t, store, packPK)
batchID, err := store.ImportBatches().Create(context.Background(), sqlite.ImportBatch{
HostID: hostPK,
PackID: packPK,
ProviderID: providerPK,
Mode: provision.ImportModePartial,
BatchStatus: provision.BatchStatusSucceeded,
AccessStatus: provision.AccessStatusSubscriptionReady,
})
if err != nil {
t.Fatalf("ImportBatches().Create() error = %v", err)
}
if _, err := store.ManagedResources().Create(context.Background(), sqlite.ManagedResource{
BatchID: batchID,
HostID: hostPK,
ResourceType: "group",
HostResourceID: "101",
ResourceName: "group-101",
}); err != nil {
t.Fatalf("ManagedResources().Create(group) error = %v", err)
}
hostRow := sqlite.Host{HostID: "host-subscription", BaseURL: server.URL, AuthType: "bearer", AuthToken: "admin-token"}
got, err := reconcileProbeAPIKey(context.Background(), store, hostRow, sqlite.ImportBatch{ID: batchID}, []sqlite.AccessClosureRecord{{
BatchID: batchID,
ClosureType: provision.AccessModeSubscription,
Status: provision.AccessStatusSubscriptionReady,
DetailsJSON: `{"subscription_users":["crm-user-1"],"subscription_days":0}`,
}})
if err != nil {
t.Fatalf("reconcileProbeAPIKey() error = %v", err)
}
if !strings.HasPrefix(got, "sk-relay-") {
t.Fatalf("reconcileProbeAPIKey() = %q, want sk-relay-*", got)
}
if assignCalls != 1 {
t.Fatalf("subscription assign calls = %d, want 1 (EnsureSubscriptionAccess only)", assignCalls)
}
}
func TestReconcileProbeAPIKeySubscriptionRequiresHostAuth(t *testing.T) {
t.Parallel()
store := openReconcileBackgroundTestStore(t)
defer closeAppTestStore(t, store)
hostPK, err := store.Hosts().Create(context.Background(), sqlite.Host{
HostID: "host-subscription",
BaseURL: "https://sub2api.example.com",
HostVersion: "0.1.126",
CapabilityProbeJSON: "{}",
AuthType: "bearer",
AuthToken: "admin-token",
})
if err != nil {
t.Fatalf("Hosts().Create() error = %v", err)
}
packPK := createBackgroundPack(t, store)
providerPK := createBackgroundProvider(t, store, packPK)
batchID, err := store.ImportBatches().Create(context.Background(), sqlite.ImportBatch{
HostID: hostPK,
PackID: packPK,
ProviderID: providerPK,
Mode: provision.ImportModePartial,
BatchStatus: provision.BatchStatusSucceeded,
AccessStatus: provision.AccessStatusSubscriptionReady,
})
if err != nil {
t.Fatalf("ImportBatches().Create() error = %v", err)
}
if _, err := store.ManagedResources().Create(context.Background(), sqlite.ManagedResource{
BatchID: batchID,
HostID: hostPK,
ResourceType: "group",
HostResourceID: "101",
ResourceName: "group-101",
}); err != nil {
t.Fatalf("ManagedResources().Create(group) error = %v", err)
}
_, err = reconcileProbeAPIKey(context.Background(), store, sqlite.Host{BaseURL: "https://sub2api.example.com", AuthType: "bearer"}, sqlite.ImportBatch{ID: batchID}, []sqlite.AccessClosureRecord{{
BatchID: batchID,
ClosureType: provision.AccessModeSubscription,
Status: provision.AccessStatusSubscriptionReady,
DetailsJSON: `{"subscription_users":["crm-user-1"],"subscription_days":30}`,
}})
if err == nil || !strings.Contains(err.Error(), "auth.token is required") {
t.Fatalf("reconcileProbeAPIKey() error = %v, want auth.token is required", err)
}
}
func createBackgroundPack(t *testing.T, store *sqlite.DB) int64 {
t.Helper()
packPK, err := store.Packs().Create(context.Background(), sqlite.Pack{
PackID: "openai-cn-pack",
Version: "1.0.0",
Checksum: "checksum-1",
Vendor: "OpenAI CN",
TargetHost: "sub2api",
MinHostVersion: "0.1.126",
MaxHostVersion: "0.2.x",
ManifestJSON: `{"pack_id":"openai-cn-pack","version":"1.0.0","target_host":"sub2api"}`,
})
if err != nil {
t.Fatalf("Packs().Create() error = %v", err)
}
return packPK
}
func createBackgroundProvider(t *testing.T, store *sqlite.DB, packPK int64) int64 {
t.Helper()
providerPK, err := store.Providers().Create(context.Background(), sqlite.Provider{
PackID: packPK,
ProviderID: "deepseek",
DisplayName: "DeepSeek",
BaseURL: "https://api.example.com",
Platform: "openai",
AccountType: "openai",
SmokeTestModel: "deepseek-chat",
ManifestJSON: `{"provider_id":"deepseek","base_url":"https://api.example.com","platform":"openai","account_type":"openai","smoke_test_model":"deepseek-chat"}`,
})
if err != nil {
t.Fatalf("Providers().Create() error = %v", err)
}
return providerPK
}
func mustGetBackgroundHost(t *testing.T, store *sqlite.DB) sqlite.Host {
t.Helper()