@a5c-ai/krate 5.0.1-staging.00fa5317c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +31 -0
- package/README.md +183 -0
- package/bin/krate-demo.mjs +23 -0
- package/bin/krate-server.mjs +14 -0
- package/dist/krate-controller-ui.json +3205 -0
- package/dist/krate-lifecycle.json +201 -0
- package/dist/krate-runtime-snapshot.json +3125 -0
- package/dist/krate-summary.json +724 -0
- package/docs/README.md +61 -0
- package/docs/agents/README.md +83 -0
- package/docs/agents/acceptance-test-matrix.md +193 -0
- package/docs/agents/agent-mux-adapter-contract.md +167 -0
- package/docs/agents/agent-mux-source-map.md +310 -0
- package/docs/agents/agent-run-memory-import-spec.md +256 -0
- package/docs/agents/agent-stack-management-spec.md +421 -0
- package/docs/agents/api-contract-spec.md +309 -0
- package/docs/agents/artifacts-writeback-spec.md +145 -0
- package/docs/agents/chart-packaging-spec.md +128 -0
- package/docs/agents/ci-orchestration-spec.md +140 -0
- package/docs/agents/context-assembly-spec.md +219 -0
- package/docs/agents/controller-reconciliation-spec.md +255 -0
- package/docs/agents/crd-schema-spec.md +315 -0
- package/docs/agents/decision-log-open-questions.md +169 -0
- package/docs/agents/developer-implementation-checklist.md +329 -0
- package/docs/agents/dispatching-design.md +262 -0
- package/docs/agents/gaps-agent-mux-to-krate-crds.md +298 -0
- package/docs/agents/glossary.md +66 -0
- package/docs/agents/implementation-blueprint.md +324 -0
- package/docs/agents/implementation-rollout-slices.md +251 -0
- package/docs/agents/memory-context-integration-spec.md +194 -0
- package/docs/agents/memory-ontology-schema-spec.md +253 -0
- package/docs/agents/memory-operations-runbook.md +121 -0
- package/docs/agents/mvp-vertical-slice-spec.md +146 -0
- package/docs/agents/observability-audit-spec.md +265 -0
- package/docs/agents/operator-runbook.md +174 -0
- package/docs/agents/org-memory-api-payload-examples.md +333 -0
- package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
- package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
- package/docs/agents/org-memory-ui-implementation-map.md +114 -0
- package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
- package/docs/agents/org-resource-model-delta-spec.md +111 -0
- package/docs/agents/org-route-resource-model-spec.md +183 -0
- package/docs/agents/org-scoping-namespace-spec.md +114 -0
- package/docs/agents/rbac-secrets-management-spec.md +406 -0
- package/docs/agents/repository-page-integration-spec.md +255 -0
- package/docs/agents/resource-contract-examples.md +808 -0
- package/docs/agents/resource-relationship-map.md +190 -0
- package/docs/agents/security-threat-model.md +188 -0
- package/docs/agents/shared-memory-company-brain-spec.md +358 -0
- package/docs/agents/storage-migration-spec.md +168 -0
- package/docs/agents/subagent-orchestration-spec.md +152 -0
- package/docs/agents/system-overview.md +88 -0
- package/docs/agents/tools-mcp-skills-spec.md +189 -0
- package/docs/agents/traceability-matrix.md +79 -0
- package/docs/agents/ui-flow-spec.md +211 -0
- package/docs/agents/ui-ux-system-spec.md +426 -0
- package/docs/agents/workspace-lifecycle-spec.md +166 -0
- package/docs/architecture-spec.md +78 -0
- package/docs/components/control-plane.md +78 -0
- package/docs/components/data-plane.md +69 -0
- package/docs/components/hooks-events.md +67 -0
- package/docs/components/identity-rbac-policy.md +73 -0
- package/docs/components/kubevela-oam.md +70 -0
- package/docs/components/operations-publishing.md +81 -0
- package/docs/components/runners-ci.md +66 -0
- package/docs/components/web-ui.md +94 -0
- package/docs/external/README.md +47 -0
- package/docs/external/bidirectional-sync-design.md +134 -0
- package/docs/external/cicd-interface.md +64 -0
- package/docs/external/external-backend-controllers.md +170 -0
- package/docs/external/external-backend-crds.md +234 -0
- package/docs/external/external-backend-ui-spec.md +151 -0
- package/docs/external/external-backend-ux-flows.md +115 -0
- package/docs/external/external-object-mapping.md +125 -0
- package/docs/external/git-forge-interface.md +68 -0
- package/docs/external/github-integration-design.md +151 -0
- package/docs/external/issue-tracking-interface.md +66 -0
- package/docs/external/provider-capability-manifests.md +204 -0
- package/docs/external/provider-catalog.md +139 -0
- package/docs/external/provider-rollout-testing.md +78 -0
- package/docs/external/research-results.md +48 -0
- package/docs/external/security-auth-permissions.md +81 -0
- package/docs/external/sync-state-machines.md +108 -0
- package/docs/external/unified-external-backend-model.md +107 -0
- package/docs/external/user-facing-changes.md +67 -0
- package/docs/gaps.md +161 -0
- package/docs/install.md +94 -0
- package/docs/krate-design.md +334 -0
- package/docs/local-minikube.md +55 -0
- package/docs/ontology/README.md +32 -0
- package/docs/ontology/bounded-contexts.md +29 -0
- package/docs/ontology/events-and-hooks.md +32 -0
- package/docs/ontology/oam-kubevela.md +32 -0
- package/docs/ontology/operations-and-release.md +25 -0
- package/docs/ontology/personas-and-actors.md +32 -0
- package/docs/ontology/policies-and-invariants.md +33 -0
- package/docs/ontology/problem-space.md +30 -0
- package/docs/ontology/resource-contracts.md +40 -0
- package/docs/ontology/resource-taxonomy.md +42 -0
- package/docs/ontology/runners-and-ci.md +29 -0
- package/docs/ontology/solution-space.md +24 -0
- package/docs/ontology/storage-and-data-boundaries.md +29 -0
- package/docs/ontology/validation-matrix.md +24 -0
- package/docs/ontology/web-ui-excellent-flows.md +32 -0
- package/docs/ontology/workflows.md +39 -0
- package/docs/ontology/world.md +35 -0
- package/docs/openapi.yaml +1275 -0
- package/docs/product-requirements.md +62 -0
- package/docs/roadmap-mvp.md +87 -0
- package/docs/system-requirements.md +90 -0
- package/docs/tests/README.md +53 -0
- package/docs/tests/agent-qa-plan.md +63 -0
- package/docs/tests/browser-ui-tests.md +62 -0
- package/docs/tests/ci-quality-gates.md +48 -0
- package/docs/tests/coverage-model.md +64 -0
- package/docs/tests/e2e-scenario-tests.md +53 -0
- package/docs/tests/fixtures-test-data.md +63 -0
- package/docs/tests/observability-reliability-tests.md +54 -0
- package/docs/tests/product-test-matrix.md +145 -0
- package/docs/tests/qa-adoption-roadmap.md +130 -0
- package/docs/tests/qa-automation-plan.md +101 -0
- package/docs/tests/security-compliance-tests.md +57 -0
- package/docs/tests/test-framework-tools.md +88 -0
- package/docs/tests/test-suite-layout.md +121 -0
- package/docs/tests/unit-integration-tests.md +48 -0
- package/docs/todo-kyverno +714 -0
- package/docs/todos.md +4 -0
- package/docs/user-stories.md +78 -0
- package/examples/minikube-demo.yaml +190 -0
- package/examples/oam-application.yaml +23 -0
- package/examples/policy-kyverno-pr-title.yaml +18 -0
- package/package.json +63 -0
- package/scripts/build.mjs +29 -0
- package/scripts/setup-minikube.mjs +65 -0
- package/scripts/smoke.mjs +37 -0
- package/scripts/validate-doc-coverage.mjs +152 -0
- package/scripts/validate-package.mjs +93 -0
- package/scripts/validate-ui.mjs +278 -0
- package/src/agent-adapter-controller.js +169 -0
- package/src/agent-approval-controller.js +170 -0
- package/src/agent-context-bundles.js +242 -0
- package/src/agent-dispatch-controller.js +209 -0
- package/src/agent-gateway-config-controller.js +147 -0
- package/src/agent-memory-controller.js +357 -0
- package/src/agent-memory-import.js +327 -0
- package/src/agent-memory-query.js +292 -0
- package/src/agent-memory-repository-source-controller.js +255 -0
- package/src/agent-mux-client.js +280 -0
- package/src/agent-permission-review.js +250 -0
- package/src/agent-project-controller.js +117 -0
- package/src/agent-provider-config-controller.js +150 -0
- package/src/agent-secret-config-grant-controller.js +282 -0
- package/src/agent-session-transcript-controller.js +189 -0
- package/src/agent-stack-controller.js +347 -0
- package/src/agent-subagent-controller.js +160 -0
- package/src/agent-transport-binding-controller.js +121 -0
- package/src/agent-trigger-controller.js +381 -0
- package/src/agent-workspace-controller.js +702 -0
- package/src/agent-writeback-controller.js +302 -0
- package/src/api-controller.js +541 -0
- package/src/argocd-gitops.js +43 -0
- package/src/async-controller.js +207 -0
- package/src/audit-controller.js +191 -0
- package/src/auth.js +307 -0
- package/src/component-catalog.js +41 -0
- package/src/control-plane.js +136 -0
- package/src/controller-client.js +72 -0
- package/src/controller-ui.js +617 -0
- package/src/data-plane.js +179 -0
- package/src/event-bus.js +61 -0
- package/src/external/conflict-controller.js +225 -0
- package/src/external/github/auth.js +96 -0
- package/src/external/github/cicd.js +180 -0
- package/src/external/github/git-forge.js +240 -0
- package/src/external/github/index.js +144 -0
- package/src/external/github/issue-tracking.js +163 -0
- package/src/external/provider-adapter.js +161 -0
- package/src/external/provider-resource-factory.js +161 -0
- package/src/external/sync-controller.js +235 -0
- package/src/external/webhook-controller.js +144 -0
- package/src/external/write-controller.js +283 -0
- package/src/gitea-backend.js +131 -0
- package/src/gitea-service.js +173 -0
- package/src/handoff.js +98 -0
- package/src/hooks-events.js +63 -0
- package/src/http-server.js +377 -0
- package/src/identity-policy.js +86 -0
- package/src/index.js +57 -0
- package/src/kubernetes-controller-async.js +511 -0
- package/src/kubernetes-controller.js +878 -0
- package/src/kubernetes-resource-gateway.js +48 -0
- package/src/notification-controller.js +178 -0
- package/src/operations.js +112 -0
- package/src/org-scoping.js +5 -0
- package/src/resource-model.js +221 -0
- package/src/runner-controller.js +272 -0
- package/src/runners-ci.js +48 -0
- package/src/runtime.js +196 -0
- package/src/snapshot-cache.js +157 -0
- package/src/web-ui.js +40 -0
- package/tests/agent-adapter-controller.test.js +361 -0
- package/tests/agent-approval-controller.test.js +173 -0
- package/tests/agent-context-bundles.test.js +278 -0
- package/tests/agent-dispatch-controller.test.js +315 -0
- package/tests/agent-gateway-config-controller.test.js +386 -0
- package/tests/agent-memory-controller.test.js +308 -0
- package/tests/agent-memory-import-snapshot.test.js +477 -0
- package/tests/agent-memory-query.test.js +404 -0
- package/tests/agent-memory-repository-source.test.js +514 -0
- package/tests/agent-mux-client.test.js +204 -0
- package/tests/agent-permission-review-v2.test.js +317 -0
- package/tests/agent-permission-review.test.js +209 -0
- package/tests/agent-project-controller.test.js +302 -0
- package/tests/agent-provider-config-controller.test.js +376 -0
- package/tests/agent-resources.test.js +228 -0
- package/tests/agent-secret-config-grant.test.js +231 -0
- package/tests/agent-session-transcript-controller.test.js +499 -0
- package/tests/agent-stack-controller.test.js +221 -0
- package/tests/agent-subagent-controller.test.js +201 -0
- package/tests/agent-transport-binding-controller.test.js +294 -0
- package/tests/agent-trigger-controller.test.js +211 -0
- package/tests/agent-trigger-routes.test.js +190 -0
- package/tests/agent-trigger-sources.test.js +245 -0
- package/tests/agent-workspace-controller.test.js +181 -0
- package/tests/agent-writeback.test.js +292 -0
- package/tests/approval-persistence.test.js +171 -0
- package/tests/async-controller.test.js +252 -0
- package/tests/audit-controller.test.js +227 -0
- package/tests/codespace-controller.test.js +318 -0
- package/tests/deployment.test.js +407 -0
- package/tests/e2e/lifecycle.test.js +117 -0
- package/tests/event-bus-integration.test.js +190 -0
- package/tests/external-github-forge.test.js +560 -0
- package/tests/external-github-issues-cicd.test.js +520 -0
- package/tests/external-integration.test.js +470 -0
- package/tests/external-persistence.test.js +340 -0
- package/tests/external-provider-adapter.test.js +365 -0
- package/tests/external-resource-model.test.js +215 -0
- package/tests/external-webhook-sync.test.js +287 -0
- package/tests/external-write-conflict.test.js +353 -0
- package/tests/gitea-service.test.js +253 -0
- package/tests/health-check-real.test.js +165 -0
- package/tests/integration/full-flow.test.js +266 -0
- package/tests/krate.test.js +756 -0
- package/tests/memory-search-wiring.test.js +270 -0
- package/tests/notification-controller.test.js +196 -0
- package/tests/notification-integration.test.js +179 -0
- package/tests/org-scoping.test.js +687 -0
- package/tests/runner-controller.test.js +327 -0
- package/tests/runner-integration.test.js +231 -0
- package/tests/session-cookie-hmac.test.js +151 -0
- package/tests/snapshot-performance.test.js +247 -0
- package/tests/sse-events.test.js +107 -0
- package/tests/webhook-trigger.test.js +198 -0
- package/tests/workspace-volumes.test.js +312 -0
- package/tests/writeback-persistence.test.js +207 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
createAgentProviderConfigController,
|
|
5
|
+
validateAgentProviderConfig,
|
|
6
|
+
createResource,
|
|
7
|
+
AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY
|
|
8
|
+
} from '../src/index.js';
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Acceptance criteria: Slice 1.2c — Agent Provider Config Controller
|
|
12
|
+
//
|
|
13
|
+
// An AgentProviderConfig defines how to connect to an LLM model provider.
|
|
14
|
+
// It specifies provider name/type, API endpoint, credential secret reference,
|
|
15
|
+
// default model, rate limit configuration, and feature flags.
|
|
16
|
+
//
|
|
17
|
+
// Supported provider types: anthropic, openai, azure-openai, google-vertex,
|
|
18
|
+
// foundry, custom.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
const VALID_PROVIDER_TYPES = ['anthropic', 'openai', 'azure-openai', 'google-vertex', 'foundry', 'custom'];
|
|
22
|
+
|
|
23
|
+
function makeProviderConfig(name, overrides = {}) {
|
|
24
|
+
return createResource('AgentProviderConfig', { name, namespace: 'krate-org-default' }, {
|
|
25
|
+
organizationRef: 'default',
|
|
26
|
+
providerType: 'anthropic',
|
|
27
|
+
endpoint: 'https://api.anthropic.com/v1',
|
|
28
|
+
credentialRef: 'anthropic-api-key',
|
|
29
|
+
defaultModel: 'claude-opus-4-5',
|
|
30
|
+
...overrides
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// 1. Factory and shape
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
test('createAgentProviderConfigController returns controller with validate, resolveEndpoint, getFeatureFlags', () => {
|
|
39
|
+
const controller = createAgentProviderConfigController();
|
|
40
|
+
assert.ok(controller, 'controller must be truthy');
|
|
41
|
+
assert.equal(typeof controller.validate, 'function', 'controller must expose validate');
|
|
42
|
+
assert.equal(typeof controller.resolveEndpoint, 'function', 'controller must expose resolveEndpoint');
|
|
43
|
+
assert.equal(typeof controller.getFeatureFlags, 'function', 'controller must expose getFeatureFlags');
|
|
44
|
+
assert.equal(controller.role, 'agent-provider-config-controller', 'controller must declare its role');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// 2. validate — happy path
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
test('validate accepts valid config with name, providerType, endpoint, credentialRef', () => {
|
|
52
|
+
const controller = createAgentProviderConfigController();
|
|
53
|
+
const config = makeProviderConfig('anthropic-provider');
|
|
54
|
+
const result = controller.validate(config);
|
|
55
|
+
|
|
56
|
+
assert.equal(result.valid, true, 'valid config must pass validation');
|
|
57
|
+
assert.ok(Array.isArray(result.errors), 'result must contain an errors array');
|
|
58
|
+
assert.equal(result.errors.length, 0, 'errors array must be empty for a valid config');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// 3. validate — missing name
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
test('validate rejects config with missing name', () => {
|
|
66
|
+
const controller = createAgentProviderConfigController();
|
|
67
|
+
const config = {
|
|
68
|
+
apiVersion: 'krate.a5c.ai/v1alpha1',
|
|
69
|
+
kind: 'AgentProviderConfig',
|
|
70
|
+
metadata: { namespace: 'krate-org-default', labels: {}, annotations: {} },
|
|
71
|
+
spec: {
|
|
72
|
+
providerType: 'anthropic',
|
|
73
|
+
endpoint: 'https://api.anthropic.com/v1',
|
|
74
|
+
credentialRef: 'anthropic-api-key'
|
|
75
|
+
},
|
|
76
|
+
status: {}
|
|
77
|
+
};
|
|
78
|
+
const result = controller.validate(config);
|
|
79
|
+
|
|
80
|
+
assert.equal(result.valid, false, 'config without a name must fail validation');
|
|
81
|
+
assert.ok(result.errors.length > 0, 'errors array must not be empty');
|
|
82
|
+
assert.ok(
|
|
83
|
+
result.errors.some((e) => /name/i.test(e)),
|
|
84
|
+
'at least one error must mention "name"'
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// 4. validate — missing providerType
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
test('validate rejects config with missing providerType', () => {
|
|
93
|
+
const controller = createAgentProviderConfigController();
|
|
94
|
+
const config = makeProviderConfig('no-type-provider');
|
|
95
|
+
delete config.spec.providerType;
|
|
96
|
+
const result = controller.validate(config);
|
|
97
|
+
|
|
98
|
+
assert.equal(result.valid, false, 'config without providerType must fail validation');
|
|
99
|
+
assert.ok(result.errors.length > 0, 'errors array must not be empty');
|
|
100
|
+
assert.ok(
|
|
101
|
+
result.errors.some((e) => /providerType/i.test(e)),
|
|
102
|
+
'at least one error must mention "providerType"'
|
|
103
|
+
);
|
|
104
|
+
assert.ok(
|
|
105
|
+
result.errors.some((e) => VALID_PROVIDER_TYPES.some((t) => e.includes(t))),
|
|
106
|
+
'error must enumerate valid provider types'
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// 5. validate — invalid providerType
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
test('validate rejects config with invalid providerType', () => {
|
|
115
|
+
const controller = createAgentProviderConfigController();
|
|
116
|
+
const config = makeProviderConfig('bad-type-provider', { providerType: 'cohere' });
|
|
117
|
+
const result = controller.validate(config);
|
|
118
|
+
|
|
119
|
+
assert.equal(result.valid, false, 'config with unsupported providerType must fail validation');
|
|
120
|
+
assert.ok(result.errors.length > 0, 'errors array must not be empty');
|
|
121
|
+
assert.ok(
|
|
122
|
+
result.errors.some((e) => /providerType/i.test(e)),
|
|
123
|
+
'at least one error must mention "providerType"'
|
|
124
|
+
);
|
|
125
|
+
assert.ok(
|
|
126
|
+
result.errors.some((e) => VALID_PROVIDER_TYPES.some((t) => e.includes(t))),
|
|
127
|
+
'error must enumerate valid provider types'
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// 6. validate — missing credentialRef (security: always require)
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
test('validate rejects config with missing credentialRef', () => {
|
|
136
|
+
const controller = createAgentProviderConfigController();
|
|
137
|
+
const config = makeProviderConfig('no-cred-provider');
|
|
138
|
+
delete config.spec.credentialRef;
|
|
139
|
+
const result = controller.validate(config);
|
|
140
|
+
|
|
141
|
+
assert.equal(result.valid, false, 'config without credentialRef must fail validation');
|
|
142
|
+
assert.ok(result.errors.length > 0, 'errors array must not be empty');
|
|
143
|
+
assert.ok(
|
|
144
|
+
result.errors.some((e) => /credentialRef/i.test(e)),
|
|
145
|
+
'at least one error must mention "credentialRef"'
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// 7. resolveEndpoint — returns endpoint from spec
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
test('resolveEndpoint returns endpoint from spec when explicitly set', () => {
|
|
154
|
+
const controller = createAgentProviderConfigController();
|
|
155
|
+
const config = makeProviderConfig('explicit-endpoint-provider', {
|
|
156
|
+
providerType: 'anthropic',
|
|
157
|
+
endpoint: 'https://custom.anthropic.proxy/v1'
|
|
158
|
+
});
|
|
159
|
+
const endpoint = controller.resolveEndpoint(config);
|
|
160
|
+
|
|
161
|
+
assert.equal(endpoint, 'https://custom.anthropic.proxy/v1', 'must return explicit spec endpoint');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// 8. resolveEndpoint — defaults for known providers
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
test('resolveEndpoint returns default endpoint for anthropic when no endpoint in spec', () => {
|
|
169
|
+
const controller = createAgentProviderConfigController();
|
|
170
|
+
const config = makeProviderConfig('anthropic-default-provider', { providerType: 'anthropic' });
|
|
171
|
+
delete config.spec.endpoint;
|
|
172
|
+
const endpoint = controller.resolveEndpoint(config);
|
|
173
|
+
|
|
174
|
+
assert.ok(endpoint.includes('api.anthropic.com'), 'must return anthropic default endpoint');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('resolveEndpoint returns default endpoint for openai when no endpoint in spec', () => {
|
|
178
|
+
const controller = createAgentProviderConfigController();
|
|
179
|
+
const config = makeProviderConfig('openai-default-provider', {
|
|
180
|
+
providerType: 'openai',
|
|
181
|
+
credentialRef: 'openai-api-key'
|
|
182
|
+
});
|
|
183
|
+
delete config.spec.endpoint;
|
|
184
|
+
const endpoint = controller.resolveEndpoint(config);
|
|
185
|
+
|
|
186
|
+
assert.ok(endpoint.includes('api.openai.com'), 'must return openai default endpoint');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// 9. getFeatureFlags — returns flags from spec with defaults
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
test('getFeatureFlags returns feature flags from spec with defaults applied', () => {
|
|
194
|
+
const controller = createAgentProviderConfigController();
|
|
195
|
+
const config = makeProviderConfig('flags-provider', {
|
|
196
|
+
featureFlags: { streaming: true, vision: false }
|
|
197
|
+
});
|
|
198
|
+
const flags = controller.getFeatureFlags(config);
|
|
199
|
+
|
|
200
|
+
assert.ok(flags, 'getFeatureFlags must return a value');
|
|
201
|
+
assert.equal(typeof flags, 'object', 'flags must be an object');
|
|
202
|
+
assert.equal(flags.streaming, true, 'streaming flag must be set from spec');
|
|
203
|
+
assert.equal(flags.vision, false, 'vision flag must be set from spec');
|
|
204
|
+
// tool_use should have a default
|
|
205
|
+
assert.ok('tool_use' in flags, 'tool_use flag must have a default');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('getFeatureFlags returns all defaults when no featureFlags in spec', () => {
|
|
209
|
+
const controller = createAgentProviderConfigController();
|
|
210
|
+
const config = makeProviderConfig('no-flags-provider');
|
|
211
|
+
const flags = controller.getFeatureFlags(config);
|
|
212
|
+
|
|
213
|
+
assert.ok(flags, 'getFeatureFlags must return a value even with no spec flags');
|
|
214
|
+
assert.ok('streaming' in flags, 'streaming must have a default');
|
|
215
|
+
assert.ok('tool_use' in flags, 'tool_use must have a default');
|
|
216
|
+
assert.ok('vision' in flags, 'vision must have a default');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// 10. getRateLimits — returns rate limit config with defaults
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
test('getRateLimits returns rate limit config with defaults', () => {
|
|
224
|
+
const controller = createAgentProviderConfigController();
|
|
225
|
+
const config = makeProviderConfig('rate-limit-provider', {
|
|
226
|
+
rateLimits: { requestsPerMinute: 60 }
|
|
227
|
+
});
|
|
228
|
+
const limits = controller.getRateLimits(config);
|
|
229
|
+
|
|
230
|
+
assert.ok(limits, 'getRateLimits must return a value');
|
|
231
|
+
assert.equal(limits.requestsPerMinute, 60, 'must use spec requestsPerMinute');
|
|
232
|
+
assert.ok(Number.isFinite(limits.tokensPerMinute), 'tokensPerMinute must have a numeric default');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('getRateLimits returns all defaults when no rateLimits in spec', () => {
|
|
236
|
+
const controller = createAgentProviderConfigController();
|
|
237
|
+
const config = makeProviderConfig('no-limits-provider');
|
|
238
|
+
const limits = controller.getRateLimits(config);
|
|
239
|
+
|
|
240
|
+
assert.ok(limits, 'getRateLimits must return a value');
|
|
241
|
+
assert.ok(Number.isFinite(limits.requestsPerMinute), 'requestsPerMinute must have a numeric default');
|
|
242
|
+
assert.ok(Number.isFinite(limits.tokensPerMinute), 'tokensPerMinute must have a numeric default');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// 11. validate — rejects null resource
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
test('validate rejects null resource with a clear error', () => {
|
|
250
|
+
const controller = createAgentProviderConfigController();
|
|
251
|
+
const result = controller.validate(null);
|
|
252
|
+
|
|
253
|
+
assert.equal(result.valid, false, 'null resource must fail validation');
|
|
254
|
+
assert.ok(result.errors.length > 0, 'errors array must not be empty');
|
|
255
|
+
assert.ok(
|
|
256
|
+
result.errors.some((e) => /null|undefined/i.test(e)),
|
|
257
|
+
'error must mention null or undefined'
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// 12. BOUNDARY — exported with correct role
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
test('AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY is exported with correct role', () => {
|
|
266
|
+
assert.ok(AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY, 'BOUNDARY must be exported');
|
|
267
|
+
assert.equal(
|
|
268
|
+
AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY.role,
|
|
269
|
+
'agent-provider-config-controller',
|
|
270
|
+
'BOUNDARY role must be "agent-provider-config-controller"'
|
|
271
|
+
);
|
|
272
|
+
assert.ok(
|
|
273
|
+
Array.isArray(AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY.owns),
|
|
274
|
+
'BOUNDARY must declare owned concerns'
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
// 13. getSupportedProviderTypes — returns valid provider types array
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
test('getSupportedProviderTypes returns valid provider types array', () => {
|
|
283
|
+
const controller = createAgentProviderConfigController();
|
|
284
|
+
const types = controller.getSupportedProviderTypes();
|
|
285
|
+
|
|
286
|
+
assert.ok(Array.isArray(types), 'getSupportedProviderTypes must return an array');
|
|
287
|
+
assert.ok(types.length >= 6, 'must return at least 6 provider types');
|
|
288
|
+
assert.ok(types.includes('anthropic'), 'must include anthropic');
|
|
289
|
+
assert.ok(types.includes('openai'), 'must include openai');
|
|
290
|
+
assert.ok(types.includes('azure-openai'), 'must include azure-openai');
|
|
291
|
+
assert.ok(types.includes('google-vertex'), 'must include google-vertex');
|
|
292
|
+
assert.ok(types.includes('foundry'), 'must include foundry');
|
|
293
|
+
assert.ok(types.includes('custom'), 'must include custom');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// 14. validateAgentProviderConfig — standalone export follows existing pattern
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
test('validateAgentProviderConfig standalone export follows existing pattern', () => {
|
|
301
|
+
assert.equal(typeof validateAgentProviderConfig, 'function', 'validateAgentProviderConfig must be a named export');
|
|
302
|
+
|
|
303
|
+
const config = makeProviderConfig('standalone-validate-provider');
|
|
304
|
+
const result = validateAgentProviderConfig(config);
|
|
305
|
+
|
|
306
|
+
assert.ok(result, 'validateAgentProviderConfig must return a result');
|
|
307
|
+
assert.ok('valid' in result, 'result must have a valid property');
|
|
308
|
+
assert.ok(Array.isArray(result.errors), 'result must have an errors array');
|
|
309
|
+
assert.equal(result.valid, true, 'a fully-specified config must pass standalone validation');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// 15. validate — all valid provider types are accepted
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
test('validate accepts all valid provider types', () => {
|
|
317
|
+
const controller = createAgentProviderConfigController();
|
|
318
|
+
for (const providerType of VALID_PROVIDER_TYPES) {
|
|
319
|
+
const config = makeProviderConfig(`${providerType}-provider`, { providerType });
|
|
320
|
+
const result = controller.validate(config);
|
|
321
|
+
assert.equal(result.valid, true, `providerType "${providerType}" must pass validation`);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// 16. validate — accumulates multiple errors
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
test('validate accumulates all errors when multiple fields are invalid', () => {
|
|
330
|
+
const controller = createAgentProviderConfigController();
|
|
331
|
+
const config = {
|
|
332
|
+
apiVersion: 'krate.a5c.ai/v1alpha1',
|
|
333
|
+
kind: 'AgentProviderConfig',
|
|
334
|
+
metadata: { namespace: 'krate-org-default', labels: {}, annotations: {} },
|
|
335
|
+
spec: { providerType: 'bad-provider' },
|
|
336
|
+
status: {}
|
|
337
|
+
};
|
|
338
|
+
const result = controller.validate(config);
|
|
339
|
+
|
|
340
|
+
assert.equal(result.valid, false, 'config with multiple invalid fields must fail');
|
|
341
|
+
assert.ok(result.errors.length >= 2, 'must accumulate at least two errors');
|
|
342
|
+
assert.ok(
|
|
343
|
+
result.errors.some((e) => /name/i.test(e)),
|
|
344
|
+
'errors must include a name error'
|
|
345
|
+
);
|
|
346
|
+
assert.ok(
|
|
347
|
+
result.errors.some((e) => /providerType/i.test(e)),
|
|
348
|
+
'errors must include a providerType error'
|
|
349
|
+
);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// 17. resolveEndpoint — throws on null resource
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
test('resolveEndpoint throws on null resource', () => {
|
|
357
|
+
const controller = createAgentProviderConfigController();
|
|
358
|
+
assert.throws(
|
|
359
|
+
() => controller.resolveEndpoint(null),
|
|
360
|
+
/null|undefined/i,
|
|
361
|
+
'resolveEndpoint must throw on null resource'
|
|
362
|
+
);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// ---------------------------------------------------------------------------
|
|
366
|
+
// 18. getFeatureFlags — throws on null resource
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
test('getFeatureFlags throws on null resource', () => {
|
|
370
|
+
const controller = createAgentProviderConfigController();
|
|
371
|
+
assert.throws(
|
|
372
|
+
() => controller.getFeatureFlags(null),
|
|
373
|
+
/null|undefined/i,
|
|
374
|
+
'getFeatureFlags must throw on null resource'
|
|
375
|
+
);
|
|
376
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
CONFIG_KINDS,
|
|
5
|
+
AGGREGATED_KINDS,
|
|
6
|
+
ALL_KINDS,
|
|
7
|
+
RESOURCE_DEFINITIONS,
|
|
8
|
+
createResource,
|
|
9
|
+
validateResource,
|
|
10
|
+
resourceSchemaForKind,
|
|
11
|
+
storageClassForKind,
|
|
12
|
+
listResourceDefinitions
|
|
13
|
+
} from '../src/resource-model.js';
|
|
14
|
+
|
|
15
|
+
const AGENT_CONFIG_KINDS = [
|
|
16
|
+
'AgentStack',
|
|
17
|
+
'AgentSubagent',
|
|
18
|
+
'AgentToolProfile',
|
|
19
|
+
'AgentMcpServer',
|
|
20
|
+
'AgentSkill',
|
|
21
|
+
'AgentTriggerRule',
|
|
22
|
+
'AgentContextLabel',
|
|
23
|
+
'KrateWorkspacePolicy',
|
|
24
|
+
'AgentServiceAccount',
|
|
25
|
+
'AgentRoleBinding',
|
|
26
|
+
'AgentSecretGrant',
|
|
27
|
+
'AgentConfigGrant',
|
|
28
|
+
'AgentAdapter',
|
|
29
|
+
'AgentTransportBinding',
|
|
30
|
+
'AgentProviderConfig',
|
|
31
|
+
'KrateProject',
|
|
32
|
+
'AgentGatewayConfig',
|
|
33
|
+
'AgentMemoryRepository',
|
|
34
|
+
'AgentMemorySource',
|
|
35
|
+
'AgentMemoryOntology',
|
|
36
|
+
'AgentMemoryAssociation',
|
|
37
|
+
'KrateWorkspace'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const AGENT_AGGREGATED_KINDS = [
|
|
41
|
+
'AgentDispatchRun',
|
|
42
|
+
'AgentDispatchAttempt',
|
|
43
|
+
'AgentSession',
|
|
44
|
+
'AgentContextBundle',
|
|
45
|
+
'KrateArtifact',
|
|
46
|
+
'AgentApproval',
|
|
47
|
+
'AgentTriggerExecution',
|
|
48
|
+
'AgentCapabilityRequirement',
|
|
49
|
+
'WorkItemSessionLink',
|
|
50
|
+
'WorkItemWorkspaceLink',
|
|
51
|
+
'AgentSessionTranscript',
|
|
52
|
+
'AgentSessionAttachment',
|
|
53
|
+
'KrateWorkspaceRuntime',
|
|
54
|
+
'AgentMemorySnapshot',
|
|
55
|
+
'AgentMemoryQuery',
|
|
56
|
+
'AgentMemoryUpdate',
|
|
57
|
+
'AgentRunMemoryImport'
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const ALL_AGENT_KINDS = [...AGENT_CONFIG_KINDS, ...AGENT_AGGREGATED_KINDS];
|
|
61
|
+
|
|
62
|
+
/** Minimal valid spec for each agent kind, satisfying requiredSpec. */
|
|
63
|
+
function minimalSpecForKind(kind) {
|
|
64
|
+
const specs = {
|
|
65
|
+
AgentStack: { organizationRef: 'default', baseAgent: 'claude-code', adapter: 'babysitter', runtimeIdentity: 'sa-agent' },
|
|
66
|
+
AgentSubagent: { organizationRef: 'default', rolePrompt: 'Code reviewer', taskKinds: ['review'] },
|
|
67
|
+
AgentToolProfile: { organizationRef: 'default', filesystemPolicy: 'read-write', approvalPolicyByTool: { shell: 'auto' } },
|
|
68
|
+
AgentMcpServer: { organizationRef: 'default', transport: 'stdio', scope: 'workspace' },
|
|
69
|
+
AgentSkill: { organizationRef: 'default', format: 'markdown', sourceRef: 'skills/debug.md' },
|
|
70
|
+
AgentTriggerRule: { organizationRef: 'default', sources: ['ci-failure'], agentStack: 'default-stack', taskKind: 'fix' },
|
|
71
|
+
AgentContextLabel: { organizationRef: 'default', promptFragment: 'Always run tests before committing', allowedSources: ['admin'] },
|
|
72
|
+
KrateWorkspacePolicy: { organizationRef: 'default', mode: 'worktree', retentionPolicy: '7d' },
|
|
73
|
+
AgentServiceAccount: { organizationRef: 'default', namespace: 'krate-agents', serviceAccountName: 'agent-runner' },
|
|
74
|
+
AgentRoleBinding: { organizationRef: 'default', subject: 'agent-runner', roleRef: 'agent-role', scope: 'namespace' },
|
|
75
|
+
AgentSecretGrant: { organizationRef: 'default', subject: 'agent-runner', secretRef: 'api-keys', purpose: 'API access' },
|
|
76
|
+
AgentConfigGrant: { organizationRef: 'default', subject: 'agent-runner', configMapRef: 'agent-config', purpose: 'Configuration' },
|
|
77
|
+
AgentDispatchRun: { organizationRef: 'default', repository: 'app', sourceRefs: ['refs/heads/main'], agentStack: 'default-stack', taskKind: 'fix' },
|
|
78
|
+
AgentDispatchAttempt: { organizationRef: 'default', agentDispatchRun: 'run-1', attemptReason: 'initial', agentStackSnapshot: { baseAgent: 'claude-code' } },
|
|
79
|
+
AgentSession: { organizationRef: 'default', agentMuxSessionId: 'sess-123', dispatchRun: 'run-1' },
|
|
80
|
+
AgentContextBundle: { organizationRef: 'default', dispatchRun: 'run-1', digest: 'sha256:abc', sources: ['repo-context'] },
|
|
81
|
+
KrateArtifact: { organizationRef: 'default', dispatchRun: 'run-1', kind: 'patch', digest: 'sha256:def' },
|
|
82
|
+
AgentApproval: { organizationRef: 'default', dispatchRun: 'run-1', action: 'write-back', requestedBy: 'agent-runner' },
|
|
83
|
+
KrateWorkspace: { organizationRef: 'default', repository: 'app', volumeSpec: { storageClassName: 'standard', capacity: '10Gi', accessModes: ['ReadWriteOnce'] } },
|
|
84
|
+
AgentTriggerExecution: { organizationRef: 'default', triggerRule: 'on-ci-fail', sourceEvent: 'pipeline-failed', decision: 'dispatch' },
|
|
85
|
+
AgentCapabilityRequirement: { organizationRef: 'default', ownerRef: 'stack-1', requiredRoles: ['shell', 'git'] },
|
|
86
|
+
WorkItemSessionLink: { organizationRef: 'default', workItemRef: 'issue-1', agentSession: 'sess-123' },
|
|
87
|
+
WorkItemWorkspaceLink: { organizationRef: 'default', workItemRef: 'issue-1', workspace: 'ws-1' },
|
|
88
|
+
AgentAdapter: { organizationRef: 'default', adapterType: 'claude-code', transport: 'stdio' },
|
|
89
|
+
AgentTransportBinding: { organizationRef: 'default', adapterRef: 'claude-code', endpoint: 'https://agent.example.test', protocol: 'https' },
|
|
90
|
+
AgentProviderConfig: { organizationRef: 'default', provider: 'anthropic', authType: 'api-key' },
|
|
91
|
+
KrateProject: { organizationRef: 'default', displayName: 'Platform' },
|
|
92
|
+
AgentGatewayConfig: { organizationRef: 'default', gatewayUrl: 'https://mux.example.test' },
|
|
93
|
+
AgentSessionTranscript: { organizationRef: 'default', sessionRef: 'sess-123', messages: [{ role: 'user', content: 'hello' }] },
|
|
94
|
+
AgentSessionAttachment: { organizationRef: 'default', sessionRef: 'sess-123', sourceType: 'upload', digest: 'sha256:abc' },
|
|
95
|
+
KrateWorkspaceRuntime: { organizationRef: 'default', workspaceRef: 'ws-1', status: 'running' },
|
|
96
|
+
AgentMemoryRepository: { organizationRef: 'default', repositoryRef: 'memory-repo', defaultBranch: 'main', layoutProfile: 'standard' },
|
|
97
|
+
AgentMemorySource: { organizationRef: 'default', repositoryRef: 'memory-repo', appliesTo: 'team:platform', include: ['decisions/**', 'runbooks/**'] },
|
|
98
|
+
AgentMemoryOntology: { organizationRef: 'default', memoryRepository: 'memory-repo', ontologyPath: '.memory/ontology.yaml' },
|
|
99
|
+
AgentMemoryAssociation: { organizationRef: 'default', memoryRef: 'decision-001', targetRef: 'issue-42', relationship: 'informs' },
|
|
100
|
+
AgentMemorySnapshot: { organizationRef: 'default', memoryRepository: 'memory-repo', requestedRef: 'refs/heads/main', resolvedCommit: 'a'.repeat(40) },
|
|
101
|
+
AgentMemoryQuery: { organizationRef: 'default', snapshotRef: 'snap-1', requester: 'agent-runner', query: 'deployment patterns' },
|
|
102
|
+
AgentMemoryUpdate: { organizationRef: 'default', memoryRepository: 'memory-repo', sourceRun: 'run-1', changes: [{ path: 'decisions/001.md', op: 'add' }] },
|
|
103
|
+
AgentRunMemoryImport: { organizationRef: 'default', memoryRepository: 'memory-repo', source: 'babysitter-run-42', include: ['journal', 'effects'] }
|
|
104
|
+
};
|
|
105
|
+
return specs[kind];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
describe('agent resource set membership', () => {
|
|
109
|
+
for (const kind of AGENT_CONFIG_KINDS) {
|
|
110
|
+
it(`${kind} is in CONFIG_KINDS`, () => {
|
|
111
|
+
assert.ok(CONFIG_KINDS.has(kind), `${kind} should be in CONFIG_KINDS`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const kind of AGENT_AGGREGATED_KINDS) {
|
|
116
|
+
it(`${kind} is in AGGREGATED_KINDS`, () => {
|
|
117
|
+
assert.ok(AGGREGATED_KINDS.has(kind), `${kind} should be in AGGREGATED_KINDS`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const kind of ALL_AGENT_KINDS) {
|
|
122
|
+
it(`${kind} is in ALL_KINDS`, () => {
|
|
123
|
+
assert.ok(ALL_KINDS.has(kind), `${kind} should be in ALL_KINDS`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('RESOURCE_DEFINITIONS for agent kinds', () => {
|
|
129
|
+
for (const kind of ALL_AGENT_KINDS) {
|
|
130
|
+
it(`${kind} has valid definition with storage, context, plural, purpose, requiredSpec`, () => {
|
|
131
|
+
const def = RESOURCE_DEFINITIONS[kind];
|
|
132
|
+
assert.ok(def, `${kind} should exist in RESOURCE_DEFINITIONS`);
|
|
133
|
+
assert.ok(['etcd', 'postgres'].includes(def.storage), `${kind} storage should be etcd or postgres`);
|
|
134
|
+
assert.ok(typeof def.context === 'string' && def.context.length > 0, `${kind} context should be a non-empty string`);
|
|
135
|
+
assert.ok(typeof def.plural === 'string' && def.plural.length > 0, `${kind} plural should be a non-empty string`);
|
|
136
|
+
assert.ok(typeof def.purpose === 'string' && def.purpose.length > 0, `${kind} purpose should be a non-empty string`);
|
|
137
|
+
assert.ok(Array.isArray(def.requiredSpec) && def.requiredSpec.length > 0, `${kind} requiredSpec should be a non-empty array`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('createResource for agent kinds', () => {
|
|
143
|
+
for (const kind of ALL_AGENT_KINDS) {
|
|
144
|
+
it(`creates a valid ${kind} resource`, () => {
|
|
145
|
+
const spec = minimalSpecForKind(kind);
|
|
146
|
+
const resource = createResource(kind, { name: `test-${kind.toLowerCase()}` }, spec);
|
|
147
|
+
assert.equal(resource.apiVersion, 'krate.a5c.ai/v1alpha1');
|
|
148
|
+
assert.equal(resource.kind, kind);
|
|
149
|
+
assert.equal(resource.metadata.name, `test-${kind.toLowerCase()}`);
|
|
150
|
+
assert.equal(resource.metadata.namespace, 'default');
|
|
151
|
+
assert.ok(resource.metadata.labels !== undefined);
|
|
152
|
+
assert.ok(resource.metadata.annotations !== undefined);
|
|
153
|
+
assert.ok(typeof resource.spec === 'object');
|
|
154
|
+
assert.ok(typeof resource.status === 'object');
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('validateResource rejects missing required spec fields for agent kinds', () => {
|
|
160
|
+
for (const kind of ALL_AGENT_KINDS) {
|
|
161
|
+
it(`throws on empty spec for ${kind}`, () => {
|
|
162
|
+
const resource = {
|
|
163
|
+
apiVersion: 'krate.a5c.ai/v1alpha1',
|
|
164
|
+
kind,
|
|
165
|
+
metadata: { name: `invalid-${kind.toLowerCase()}` },
|
|
166
|
+
spec: {},
|
|
167
|
+
status: {}
|
|
168
|
+
};
|
|
169
|
+
const def = RESOURCE_DEFINITIONS[kind];
|
|
170
|
+
assert.throws(
|
|
171
|
+
() => validateResource(resource),
|
|
172
|
+
(err) => {
|
|
173
|
+
assert.ok(err.message.includes(`${kind} spec.${def.requiredSpec[0]} is required`));
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('resourceSchemaForKind for agent kinds', () => {
|
|
182
|
+
for (const kind of ALL_AGENT_KINDS) {
|
|
183
|
+
it(`returns correct schema for ${kind}`, () => {
|
|
184
|
+
const schema = resourceSchemaForKind(kind);
|
|
185
|
+
assert.equal(schema.apiVersion, 'krate.a5c.ai/v1alpha1');
|
|
186
|
+
assert.equal(schema.kind, kind);
|
|
187
|
+
assert.ok(typeof schema.plural === 'string');
|
|
188
|
+
assert.ok(['etcd', 'postgres'].includes(schema.storage));
|
|
189
|
+
assert.ok(Array.isArray(schema.required.metadata));
|
|
190
|
+
assert.ok(schema.required.metadata.includes('name'));
|
|
191
|
+
assert.ok(Array.isArray(schema.required.spec));
|
|
192
|
+
assert.ok(schema.required.spec.length > 0);
|
|
193
|
+
assert.deepEqual(schema.status, ['storage', 'phase', 'conditions']);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('storageClassForKind for agent kinds', () => {
|
|
199
|
+
for (const kind of AGENT_CONFIG_KINDS) {
|
|
200
|
+
it(`${kind} returns etcd`, () => {
|
|
201
|
+
assert.equal(storageClassForKind(kind), 'etcd');
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const kind of AGENT_AGGREGATED_KINDS) {
|
|
206
|
+
it(`${kind} returns postgres`, () => {
|
|
207
|
+
assert.equal(storageClassForKind(kind), 'postgres');
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('kind set counts', () => {
|
|
213
|
+
it('CONFIG_KINDS has 46 members', () => {
|
|
214
|
+
assert.equal(CONFIG_KINDS.size, 46);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('AGGREGATED_KINDS has 29 members', () => {
|
|
218
|
+
assert.equal(AGGREGATED_KINDS.size, 29);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('ALL_KINDS has 75 members', () => {
|
|
222
|
+
assert.equal(ALL_KINDS.size, 75);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('listResourceDefinitions returns 75 definitions', () => {
|
|
226
|
+
assert.equal(listResourceDefinitions().length, 75);
|
|
227
|
+
});
|
|
228
|
+
});
|