Expand coverage for runtime and sqlite paths
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user