@camstack/server 1.0.0 → 1.0.1
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/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
- package/dist/api/addon-upload.js +441 -0
- package/dist/api/addons-custom.router.js +91 -0
- package/dist/api/auth-whoami.js +55 -0
- package/dist/api/bridge-addons.router.js +109 -0
- package/dist/api/capabilities.router.js +229 -0
- package/dist/api/core/addon-settings.router.js +117 -0
- package/dist/api/core/agents.router.js +73 -0
- package/dist/api/core/auth.router.js +286 -0
- package/dist/api/core/bulk-update-coordinator.js +229 -0
- package/dist/api/core/cap-providers.js +1124 -0
- package/dist/api/core/capabilities.router.js +138 -0
- package/dist/api/core/collection-preference.js +17 -0
- package/dist/api/core/event-bus-proxy.router.js +45 -0
- package/dist/api/core/hwaccel.router.js +91 -0
- package/dist/api/core/live-events.router.js +61 -0
- package/dist/api/core/logs.router.js +172 -0
- package/dist/api/core/notifications.router.js +67 -0
- package/dist/api/core/repl.router.js +35 -0
- package/dist/api/core/settings-backend.router.js +121 -0
- package/dist/api/core/stream-probe.router.js +58 -0
- package/dist/api/core/system-events.router.js +100 -0
- package/dist/api/health/health.routes.js +68 -0
- package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
- package/dist/api/oauth2/oauth2-routes.js +219 -0
- package/dist/api/trpc/cap-mount-helpers.js +194 -0
- package/dist/api/trpc/cap-route-error-formatter.js +133 -0
- package/dist/api/trpc/client-ip.js +147 -0
- package/dist/api/trpc/core-cap-bridge.js +115 -0
- package/dist/api/trpc/generated-cap-mounts.js +388 -0
- package/dist/api/trpc/generated-cap-routers.js +7635 -0
- package/dist/api/trpc/scope-access.js +93 -0
- package/dist/api/trpc/trpc.context.js +184 -0
- package/dist/api/trpc/trpc.middleware.js +139 -0
- package/dist/api/trpc/trpc.router.js +188 -0
- package/dist/auth/session-cookie.js +47 -0
- package/dist/boot/boot-config.js +241 -0
- package/dist/boot/integration-id-backfill.js +76 -0
- package/dist/boot/post-boot.service.js +85 -0
- package/dist/core/addon/addon-call-gateway.js +99 -0
- package/dist/core/addon/addon-package.service.js +1560 -0
- package/dist/core/addon/addon-registry.service.js +2739 -0
- package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
- package/dist/core/addon/addon-search.service.js +62 -0
- package/dist/core/addon/addon-settings-provider.js +102 -0
- package/dist/core/addon/addon.tokens.js +5 -0
- package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
- package/dist/core/addon-pages/addon-pages.service.js +107 -0
- package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
- package/dist/core/agent/agent-registry.service.js +477 -0
- package/dist/core/auth/auth.service.js +10 -0
- package/dist/core/capability/capability.service.js +58 -0
- package/dist/core/config/config.schema.js +7 -0
- package/dist/core/config/config.service.js +10 -0
- package/dist/core/events/event-bus.service.js +83 -0
- package/dist/core/feature/feature.service.js +10 -0
- package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
- package/dist/core/logging/log-ring-buffer.js +6 -0
- package/dist/core/logging/logging.service.js +130 -0
- package/dist/core/logging/scoped-logger.js +6 -0
- package/dist/core/moleculer/cap-call-fn.js +50 -0
- package/dist/core/moleculer/cap-route-authority.js +122 -0
- package/dist/core/moleculer/moleculer.service.js +898 -0
- package/dist/core/network/network-quality.service.js +7 -0
- package/dist/core/notification/notification-wrapper.service.js +33 -0
- package/dist/core/notification/toast-wrapper.service.js +25 -0
- package/dist/core/provider/provider.tokens.js +4 -0
- package/dist/core/repl/repl-engine.service.js +140 -0
- package/dist/core/storage/fs-storage-backend.js +6 -0
- package/dist/core/storage/storage-location-manager.js +6 -0
- package/dist/core/storage/storage.service.js +7 -0
- package/dist/core/streaming/stream-probe.service.js +209 -0
- package/dist/core/topology/topology-emitter.service.js +106 -0
- package/dist/launcher.js +325 -0
- package/dist/main.js +1098 -0
- package/dist/manual-boot.js +227 -0
- package/package.json +5 -1
- package/src/__tests__/addon-install-e2e.test.ts +0 -74
- package/src/__tests__/addon-pages-e2e.test.ts +0 -200
- package/src/__tests__/addon-route-session.test.ts +0 -17
- package/src/__tests__/addon-settings-router.spec.ts +0 -67
- package/src/__tests__/addon-upload.spec.ts +0 -475
- package/src/__tests__/agent-registry.spec.ts +0 -179
- package/src/__tests__/agent-status-page.spec.ts +0 -82
- package/src/__tests__/auth-session-cookie.test.ts +0 -48
- package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
- package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
- package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
- package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
- package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
- package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
- package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
- package/src/__tests__/cap-route-adapter.spec.ts +0 -302
- package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
- package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
- package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
- package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
- package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
- package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
- package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
- package/src/__tests__/cap-routers/harness.ts +0 -163
- package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
- package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
- package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
- package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
- package/src/__tests__/capability-e2e.test.ts +0 -384
- package/src/__tests__/cli-e2e.test.ts +0 -150
- package/src/__tests__/core-cap-bridge.spec.ts +0 -91
- package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
- package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
- package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
- package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
- package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
- package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
- package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
- package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
- package/src/__tests__/framework-allowlist.spec.ts +0 -96
- package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
- package/src/__tests__/https-e2e.test.ts +0 -124
- package/src/__tests__/lifecycle-e2e.test.ts +0 -189
- package/src/__tests__/live-events-subscription.spec.ts +0 -149
- package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
- package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
- package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
- package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
- package/src/__tests__/native-cap-route.spec.ts +0 -427
- package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
- package/src/__tests__/post-boot-restart.spec.ts +0 -161
- package/src/__tests__/singleton-contention.test.ts +0 -499
- package/src/__tests__/streaming-diagnostic.test.ts +0 -615
- package/src/__tests__/streaming-scale.test.ts +0 -314
- package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
- package/src/__tests__/uds-log-ingest.spec.ts +0 -183
- package/src/api/__tests__/addons-custom.spec.ts +0 -148
- package/src/api/__tests__/capabilities.router.test.ts +0 -56
- package/src/api/addon-upload.ts +0 -529
- package/src/api/addons-custom.router.ts +0 -101
- package/src/api/auth-whoami.ts +0 -101
- package/src/api/bridge-addons.router.ts +0 -122
- package/src/api/capabilities.router.ts +0 -265
- package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
- package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
- package/src/api/core/addon-settings.router.ts +0 -127
- package/src/api/core/agents.router.ts +0 -86
- package/src/api/core/auth.router.ts +0 -322
- package/src/api/core/bulk-update-coordinator.ts +0 -305
- package/src/api/core/cap-providers.ts +0 -1339
- package/src/api/core/capabilities.router.ts +0 -149
- package/src/api/core/collection-preference.ts +0 -40
- package/src/api/core/event-bus-proxy.router.ts +0 -45
- package/src/api/core/hwaccel.router.ts +0 -108
- package/src/api/core/live-events.router.ts +0 -67
- package/src/api/core/logs.router.ts +0 -195
- package/src/api/core/notifications.router.ts +0 -66
- package/src/api/core/repl.router.ts +0 -39
- package/src/api/core/settings-backend.router.ts +0 -140
- package/src/api/core/stream-probe.router.ts +0 -57
- package/src/api/core/system-events.router.ts +0 -125
- package/src/api/health/health.routes.ts +0 -117
- package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
- package/src/api/oauth2/oauth2-routes.ts +0 -281
- package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
- package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
- package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
- package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
- package/src/api/trpc/cap-mount-helpers.ts +0 -245
- package/src/api/trpc/cap-route-error-formatter.ts +0 -171
- package/src/api/trpc/client-ip.ts +0 -147
- package/src/api/trpc/core-cap-bridge.ts +0 -154
- package/src/api/trpc/generated-cap-mounts.ts +0 -1240
- package/src/api/trpc/generated-cap-routers.ts +0 -11523
- package/src/api/trpc/scope-access.ts +0 -110
- package/src/api/trpc/trpc.context.ts +0 -258
- package/src/api/trpc/trpc.middleware.ts +0 -146
- package/src/api/trpc/trpc.router.ts +0 -389
- package/src/auth/session-cookie.ts +0 -54
- package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
- package/src/boot/boot-config.ts +0 -259
- package/src/boot/integration-id-backfill.ts +0 -109
- package/src/boot/post-boot.service.ts +0 -105
- package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
- package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
- package/src/core/addon/addon-call-gateway.ts +0 -171
- package/src/core/addon/addon-package.service.ts +0 -1787
- package/src/core/addon/addon-registry.service.ts +0 -3130
- package/src/core/addon/addon-search.service.ts +0 -91
- package/src/core/addon/addon-settings-provider.ts +0 -220
- package/src/core/addon/addon.tokens.ts +0 -2
- package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
- package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
- package/src/core/addon-pages/addon-pages.service.ts +0 -82
- package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
- package/src/core/agent/agent-registry.service.ts +0 -529
- package/src/core/auth/auth.service.spec.ts +0 -86
- package/src/core/auth/auth.service.ts +0 -8
- package/src/core/capability/capability.service.ts +0 -66
- package/src/core/config/config.schema.ts +0 -3
- package/src/core/config/config.service.spec.ts +0 -175
- package/src/core/config/config.service.ts +0 -7
- package/src/core/events/event-bus.service.spec.ts +0 -235
- package/src/core/events/event-bus.service.ts +0 -89
- package/src/core/feature/feature.service.spec.ts +0 -99
- package/src/core/feature/feature.service.ts +0 -8
- package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
- package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
- package/src/core/logging/log-ring-buffer.ts +0 -3
- package/src/core/logging/logging.service.spec.ts +0 -287
- package/src/core/logging/logging.service.ts +0 -143
- package/src/core/logging/scoped-logger.ts +0 -3
- package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
- package/src/core/moleculer/cap-call-fn.ts +0 -107
- package/src/core/moleculer/cap-route-authority.ts +0 -194
- package/src/core/moleculer/moleculer.service.ts +0 -1072
- package/src/core/network/network-quality.service.spec.ts +0 -53
- package/src/core/network/network-quality.service.ts +0 -5
- package/src/core/notification/notification-wrapper.service.ts +0 -34
- package/src/core/notification/toast-wrapper.service.ts +0 -27
- package/src/core/provider/provider.tokens.ts +0 -1
- package/src/core/repl/repl-engine.service.spec.ts +0 -444
- package/src/core/repl/repl-engine.service.ts +0 -155
- package/src/core/storage/fs-storage-backend.spec.ts +0 -70
- package/src/core/storage/fs-storage-backend.ts +0 -3
- package/src/core/storage/storage-location-manager.spec.ts +0 -130
- package/src/core/storage/storage-location-manager.ts +0 -3
- package/src/core/storage/storage.service.spec.ts +0 -73
- package/src/core/storage/storage.service.ts +0 -3
- package/src/core/streaming/stream-probe.service.ts +0 -221
- package/src/core/topology/topology-emitter.service.ts +0 -105
- package/src/launcher.ts +0 -314
- package/src/main.ts +0 -1245
- package/src/manual-boot.ts +0 -301
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -33
- package/vitest.config.ts +0 -26
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task G2 — single cap-ownership authority (consolidation)
|
|
3
|
-
*
|
|
4
|
-
* This test pins the consolidated behavior: the `setNativeFallback` closure
|
|
5
|
-
* consults the CapRouteResolver FIRST for hub-local native caps, and only
|
|
6
|
-
* falls through to `resolveNativeCapOwnerSync` for remote native caps.
|
|
7
|
-
*
|
|
8
|
-
* The three canonical cases:
|
|
9
|
-
* (a) Hub-local child device-scoped native cap → resolver classifies hub-local-uds;
|
|
10
|
-
* `resolveNativeCapOwnerSync` is NOT consulted.
|
|
11
|
-
* (b) Remote device-scoped native cap → resolver does not find hub-local;
|
|
12
|
-
* `resolveNativeCapOwnerSync` is consulted and `buildNativeCapProxy` is used.
|
|
13
|
-
* (c) Cap absent entirely → neither resolver nor resolveNativeCapOwnerSync
|
|
14
|
-
* know the owner; fallback returns null.
|
|
15
|
-
*
|
|
16
|
-
* Behavior parity: the old flow called resolveNativeCapOwnerSync first and then
|
|
17
|
-
* forked on nodeId prefix. The new flow queries the resolver first for hub-local
|
|
18
|
-
* (skipping resolveNativeCapOwnerSync in that branch) and falls through to
|
|
19
|
-
* resolveNativeCapOwnerSync only for the remote branch. Both flows return
|
|
20
|
-
* identical proxy objects / null for the same inputs.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
24
|
-
import type {
|
|
25
|
-
NodeCapAuthority,
|
|
26
|
-
HubLocalChildDispatcher,
|
|
27
|
-
CapRouteResolverDeps,
|
|
28
|
-
} from '@camstack/kernel'
|
|
29
|
-
import { CapRouteResolver, CapRouteError } from '@camstack/kernel'
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Helper: build the consolidated native-cap fallback (the G2 implementation)
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The G2-consolidated `setNativeFallback` implementation.
|
|
37
|
-
*
|
|
38
|
-
* Resolution order:
|
|
39
|
-
* 1. Try the resolver for hub-local (avoids resolveNativeCapOwnerSync entirely
|
|
40
|
-
* when the resolver's snapshot knows the child).
|
|
41
|
-
* 2. If no hub-local route: consult resolveNativeCapOwnerSync for remote caps.
|
|
42
|
-
* 3. If not remote either: return null.
|
|
43
|
-
*/
|
|
44
|
-
function buildConsolidatedNativeFallback(opts: {
|
|
45
|
-
readonly resolver: CapRouteResolver | null
|
|
46
|
-
readonly hubNodeId: string
|
|
47
|
-
readonly resolveNativeCapOwnerSync: (
|
|
48
|
-
capName: string,
|
|
49
|
-
deviceId: number,
|
|
50
|
-
) => { addonId: string; nodeId: string } | null
|
|
51
|
-
readonly buildNativeCapProxy: (
|
|
52
|
-
addonId: string,
|
|
53
|
-
capName: string,
|
|
54
|
-
deviceId: number,
|
|
55
|
-
) => Record<string, unknown>
|
|
56
|
-
}): (capName: string, deviceId: number) => unknown | null {
|
|
57
|
-
const { resolver, hubNodeId, resolveNativeCapOwnerSync, buildNativeCapProxy } = opts
|
|
58
|
-
|
|
59
|
-
return (capName: string, deviceId: number): unknown | null => {
|
|
60
|
-
// 1. Hub-local: resolver is the single authority. No resolveNativeCapOwnerSync needed.
|
|
61
|
-
if (resolver !== null) {
|
|
62
|
-
try {
|
|
63
|
-
const route = resolver.resolveCapRoute(capName, { nodeId: hubNodeId, deviceId })
|
|
64
|
-
if (route.kind === 'hub-local-uds') {
|
|
65
|
-
// Build a proxy that routes every method through the resolver.
|
|
66
|
-
const proxy: Record<string, (input: unknown) => Promise<unknown>> = {}
|
|
67
|
-
// Proxy is returned as a property-access object (same shape as before)
|
|
68
|
-
return new Proxy(proxy, {
|
|
69
|
-
get(_target, property): ((input: unknown) => Promise<unknown>) | undefined {
|
|
70
|
-
if (typeof property !== 'string') return undefined
|
|
71
|
-
return (input: unknown): Promise<unknown> => {
|
|
72
|
-
const mergedInput =
|
|
73
|
-
typeof input === 'object' && input !== null
|
|
74
|
-
? { ...input, deviceId }
|
|
75
|
-
: { deviceId }
|
|
76
|
-
const r = resolver.resolveCapRoute(capName, { nodeId: hubNodeId, deviceId })
|
|
77
|
-
return resolver.dispatch(r, property, mergedInput)
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
} catch (err) {
|
|
83
|
-
// resolver throws CapRouteError for no-provider/node-offline — fall through to remote
|
|
84
|
-
if (!(err instanceof CapRouteError)) throw err
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 2. Remote: consult resolveNativeCapOwnerSync (includes push-fed remoteNativeCaps).
|
|
89
|
-
const owner = resolveNativeCapOwnerSync(capName, deviceId)
|
|
90
|
-
if (!owner) return null
|
|
91
|
-
|
|
92
|
-
// Only build a remote proxy for genuinely-remote nodes; hub-local already handled above.
|
|
93
|
-
if (!owner.nodeId.startsWith(`${hubNodeId}/`)) {
|
|
94
|
-
return buildNativeCapProxy(owner.addonId, capName, deviceId)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Edge case: resolver was null OR didn't find the hub-local route but resolveNativeCapOwnerSync
|
|
98
|
-
// says hub-local. This can happen during startup before the resolver is initialised.
|
|
99
|
-
// Fall through to null — the caller will retry when the resolver is ready.
|
|
100
|
-
return null
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ---------------------------------------------------------------------------
|
|
105
|
-
// Helpers to build test doubles
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
|
-
|
|
108
|
-
interface FakeHubLocalRegistry extends HubLocalChildDispatcher {
|
|
109
|
-
readonly callSpy: ReturnType<typeof vi.fn>
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function makeHubLocalRegistry(
|
|
113
|
-
caps: ReadonlyMap<string, ReadonlyMap<number | 'singleton', string>>,
|
|
114
|
-
): FakeHubLocalRegistry {
|
|
115
|
-
const callSpy = vi.fn(async (_childId: string, _input: unknown) => ({ ok: true, from: 'uds' }))
|
|
116
|
-
return {
|
|
117
|
-
resolveChildId: (capName: string, deviceId?: number): string | null => {
|
|
118
|
-
const capMap = caps.get(capName)
|
|
119
|
-
if (capMap === undefined) return null
|
|
120
|
-
if (deviceId !== undefined) {
|
|
121
|
-
const deviceSpecific = capMap.get(deviceId)
|
|
122
|
-
if (deviceSpecific !== undefined) return deviceSpecific
|
|
123
|
-
}
|
|
124
|
-
return capMap.get('singleton') ?? null
|
|
125
|
-
},
|
|
126
|
-
callCapOnChild: callSpy,
|
|
127
|
-
callSpy,
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
interface NativeCapSpec {
|
|
132
|
-
readonly nodeId: string
|
|
133
|
-
readonly addonId: string
|
|
134
|
-
readonly capName: string
|
|
135
|
-
readonly deviceId: number
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function makeNodeAuthority(
|
|
139
|
-
systemCaps: ReadonlyMap<string, { addonId: string; nodeId: string }>,
|
|
140
|
-
onlineNodes: ReadonlySet<string>,
|
|
141
|
-
nativeCaps: readonly NativeCapSpec[],
|
|
142
|
-
): NodeCapAuthority {
|
|
143
|
-
return {
|
|
144
|
-
nodeKnowsCap: (nodeId: string, capName: string): boolean => {
|
|
145
|
-
const sys = systemCaps.get(capName)
|
|
146
|
-
if (sys !== undefined && sys.nodeId === nodeId) return true
|
|
147
|
-
return nativeCaps.some((n) => n.nodeId === nodeId && n.capName === capName)
|
|
148
|
-
},
|
|
149
|
-
nodeIsAgent: (nodeId: string): boolean => nodeId !== 'hub' && !nodeId.includes('/'),
|
|
150
|
-
nodeOnline: (nodeId: string): boolean => onlineNodes.has(nodeId),
|
|
151
|
-
listNodeIds: (): readonly string[] => {
|
|
152
|
-
const ids = new Set<string>()
|
|
153
|
-
for (const spec of systemCaps.values()) ids.add(spec.nodeId)
|
|
154
|
-
for (const n of nativeCaps) ids.add(n.nodeId)
|
|
155
|
-
return [...ids]
|
|
156
|
-
},
|
|
157
|
-
getAddonId: (nodeId: string, capName: string): string | null => {
|
|
158
|
-
const sys = systemCaps.get(capName)
|
|
159
|
-
if (sys !== undefined && sys.nodeId === nodeId) return sys.addonId
|
|
160
|
-
const nat = nativeCaps.find((n) => n.nodeId === nodeId && n.capName === capName)
|
|
161
|
-
return nat?.addonId ?? null
|
|
162
|
-
},
|
|
163
|
-
getAgentChildId: (): string | null => null,
|
|
164
|
-
isNativeCap: (nodeId: string, capName: string, deviceId?: number): boolean => {
|
|
165
|
-
if (deviceId !== undefined) {
|
|
166
|
-
return nativeCaps.some(
|
|
167
|
-
(n) => n.nodeId === nodeId && n.capName === capName && n.deviceId === deviceId,
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
return nativeCaps.some((n) => n.nodeId === nodeId && n.capName === capName)
|
|
171
|
-
},
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const HUB_NODE_ID = 'hub'
|
|
176
|
-
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
// Test (a): hub-local child device-scoped native cap
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
|
|
181
|
-
describe('G2 — (a) hub-local device-scoped native cap: resolver is consulted first, resolveNativeCapOwnerSync is NOT called', () => {
|
|
182
|
-
it('builds a proxy without consulting resolveNativeCapOwnerSync when resolver finds hub-local-uds', async () => {
|
|
183
|
-
const hubLocalRegistry = makeHubLocalRegistry(
|
|
184
|
-
new Map([['ptz', new Map([[7, 'provider-reolink']])]]),
|
|
185
|
-
)
|
|
186
|
-
const nativeCaps: NativeCapSpec[] = [
|
|
187
|
-
{
|
|
188
|
-
nodeId: 'hub/provider-reolink',
|
|
189
|
-
addonId: 'addon-provider-reolink',
|
|
190
|
-
capName: 'ptz',
|
|
191
|
-
deviceId: 7,
|
|
192
|
-
},
|
|
193
|
-
]
|
|
194
|
-
const nodeAuthority = makeNodeAuthority(
|
|
195
|
-
new Map(),
|
|
196
|
-
new Set(['hub/provider-reolink']),
|
|
197
|
-
nativeCaps,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
const deps: CapRouteResolverDeps = {
|
|
201
|
-
hubNodeId: HUB_NODE_ID,
|
|
202
|
-
broker: { call: vi.fn(), waitForServices: vi.fn() },
|
|
203
|
-
hubLocalRegistry,
|
|
204
|
-
nodeAuthority,
|
|
205
|
-
inProcessProviders: () => null,
|
|
206
|
-
}
|
|
207
|
-
const resolver = new CapRouteResolver(deps)
|
|
208
|
-
|
|
209
|
-
const resolveNativeCapOwnerSync = vi.fn()
|
|
210
|
-
const buildNativeCapProxy = vi.fn()
|
|
211
|
-
|
|
212
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
213
|
-
resolver,
|
|
214
|
-
hubNodeId: HUB_NODE_ID,
|
|
215
|
-
resolveNativeCapOwnerSync,
|
|
216
|
-
buildNativeCapProxy,
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
const proxy = fallback('ptz', 7)
|
|
220
|
-
|
|
221
|
-
// Proxy is NOT null — hub-local route was found
|
|
222
|
-
expect(proxy).not.toBeNull()
|
|
223
|
-
expect(typeof proxy).toBe('object')
|
|
224
|
-
|
|
225
|
-
// resolveNativeCapOwnerSync was NOT called (resolver handled it)
|
|
226
|
-
expect(resolveNativeCapOwnerSync).not.toHaveBeenCalled()
|
|
227
|
-
// buildNativeCapProxy was NOT called (not a remote cap)
|
|
228
|
-
expect(buildNativeCapProxy).not.toHaveBeenCalled()
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('proxy built by the consolidated fallback routes to callCapOnChild via the resolver', async () => {
|
|
232
|
-
const hubLocalRegistry = makeHubLocalRegistry(
|
|
233
|
-
new Map([['ptz', new Map([[7, 'provider-reolink']])]]),
|
|
234
|
-
)
|
|
235
|
-
const nativeCaps: NativeCapSpec[] = [
|
|
236
|
-
{
|
|
237
|
-
nodeId: 'hub/provider-reolink',
|
|
238
|
-
addonId: 'addon-provider-reolink',
|
|
239
|
-
capName: 'ptz',
|
|
240
|
-
deviceId: 7,
|
|
241
|
-
},
|
|
242
|
-
]
|
|
243
|
-
const nodeAuthority = makeNodeAuthority(
|
|
244
|
-
new Map(),
|
|
245
|
-
new Set(['hub/provider-reolink']),
|
|
246
|
-
nativeCaps,
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
const deps: CapRouteResolverDeps = {
|
|
250
|
-
hubNodeId: HUB_NODE_ID,
|
|
251
|
-
broker: { call: vi.fn(), waitForServices: vi.fn() },
|
|
252
|
-
hubLocalRegistry,
|
|
253
|
-
nodeAuthority,
|
|
254
|
-
inProcessProviders: () => null,
|
|
255
|
-
}
|
|
256
|
-
const resolver = new CapRouteResolver(deps)
|
|
257
|
-
|
|
258
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
259
|
-
resolver,
|
|
260
|
-
hubNodeId: HUB_NODE_ID,
|
|
261
|
-
resolveNativeCapOwnerSync: vi.fn(),
|
|
262
|
-
buildNativeCapProxy: vi.fn(),
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
const proxy = fallback('ptz', 7) as Record<string, (args: unknown) => Promise<unknown>>
|
|
266
|
-
expect(proxy).not.toBeNull()
|
|
267
|
-
|
|
268
|
-
// Invoke a method through the proxy — should reach callCapOnChild
|
|
269
|
-
const result = await proxy['move']!({ pan: 10 })
|
|
270
|
-
expect(result).toEqual({ ok: true, from: 'uds' })
|
|
271
|
-
expect(hubLocalRegistry.callSpy).toHaveBeenCalledOnce()
|
|
272
|
-
const [calledChildId, calledInput] = hubLocalRegistry.callSpy.mock.calls[0] as [string, unknown]
|
|
273
|
-
expect(calledChildId).toBe('provider-reolink')
|
|
274
|
-
expect(calledInput).toMatchObject({ capName: 'ptz', method: 'move', deviceId: 7 })
|
|
275
|
-
})
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
// ---------------------------------------------------------------------------
|
|
279
|
-
// Test (b): remote device-scoped native cap
|
|
280
|
-
// ---------------------------------------------------------------------------
|
|
281
|
-
|
|
282
|
-
describe('G2 — (b) remote device-scoped native cap: resolver does not find hub-local, resolveNativeCapOwnerSync IS called', () => {
|
|
283
|
-
it('falls through to resolveNativeCapOwnerSync and calls buildNativeCapProxy for remote caps', () => {
|
|
284
|
-
// No hub-local registry for this cap+device (resolver will throw no-provider for hub)
|
|
285
|
-
const hubLocalRegistry = makeHubLocalRegistry(new Map()) // empty: no hub-local caps
|
|
286
|
-
const nodeAuthority = makeNodeAuthority(new Map(), new Set([]), [])
|
|
287
|
-
|
|
288
|
-
const deps: CapRouteResolverDeps = {
|
|
289
|
-
hubNodeId: HUB_NODE_ID,
|
|
290
|
-
broker: { call: vi.fn(), waitForServices: vi.fn() },
|
|
291
|
-
hubLocalRegistry,
|
|
292
|
-
nodeAuthority,
|
|
293
|
-
inProcessProviders: () => null,
|
|
294
|
-
}
|
|
295
|
-
const resolver = new CapRouteResolver(deps)
|
|
296
|
-
|
|
297
|
-
const resolveNativeCapOwnerSync = vi.fn().mockReturnValue({
|
|
298
|
-
addonId: 'addon-provider-reolink',
|
|
299
|
-
nodeId: 'dev-agent-0/provider-reolink', // remote node
|
|
300
|
-
})
|
|
301
|
-
const remoteProxy = { remote: true }
|
|
302
|
-
const buildNativeCapProxy = vi.fn().mockReturnValue(remoteProxy)
|
|
303
|
-
|
|
304
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
305
|
-
resolver,
|
|
306
|
-
hubNodeId: HUB_NODE_ID,
|
|
307
|
-
resolveNativeCapOwnerSync,
|
|
308
|
-
buildNativeCapProxy,
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
const result = fallback('ptz', 7)
|
|
312
|
-
|
|
313
|
-
// resolveNativeCapOwnerSync was called (no hub-local route in resolver)
|
|
314
|
-
expect(resolveNativeCapOwnerSync).toHaveBeenCalledWith('ptz', 7)
|
|
315
|
-
// buildNativeCapProxy was called with the remote owner's addonId
|
|
316
|
-
expect(buildNativeCapProxy).toHaveBeenCalledWith('addon-provider-reolink', 'ptz', 7)
|
|
317
|
-
// The remote proxy is returned
|
|
318
|
-
expect(result).toBe(remoteProxy)
|
|
319
|
-
})
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
// ---------------------------------------------------------------------------
|
|
323
|
-
// Test (c): cap absent entirely
|
|
324
|
-
// ---------------------------------------------------------------------------
|
|
325
|
-
|
|
326
|
-
describe('G2 — (c) cap absent: returns null (no hub-local, no remote owner)', () => {
|
|
327
|
-
it('returns null when resolver finds no hub-local route AND resolveNativeCapOwnerSync returns null', () => {
|
|
328
|
-
const hubLocalRegistry = makeHubLocalRegistry(new Map())
|
|
329
|
-
const nodeAuthority = makeNodeAuthority(new Map(), new Set([]), [])
|
|
330
|
-
|
|
331
|
-
const deps: CapRouteResolverDeps = {
|
|
332
|
-
hubNodeId: HUB_NODE_ID,
|
|
333
|
-
broker: { call: vi.fn(), waitForServices: vi.fn() },
|
|
334
|
-
hubLocalRegistry,
|
|
335
|
-
nodeAuthority,
|
|
336
|
-
inProcessProviders: () => null,
|
|
337
|
-
}
|
|
338
|
-
const resolver = new CapRouteResolver(deps)
|
|
339
|
-
|
|
340
|
-
const resolveNativeCapOwnerSync = vi.fn().mockReturnValue(null)
|
|
341
|
-
const buildNativeCapProxy = vi.fn()
|
|
342
|
-
|
|
343
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
344
|
-
resolver,
|
|
345
|
-
hubNodeId: HUB_NODE_ID,
|
|
346
|
-
resolveNativeCapOwnerSync,
|
|
347
|
-
buildNativeCapProxy,
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
const result = fallback('ptz', 7)
|
|
351
|
-
|
|
352
|
-
expect(result).toBeNull()
|
|
353
|
-
expect(buildNativeCapProxy).not.toHaveBeenCalled()
|
|
354
|
-
})
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
// ---------------------------------------------------------------------------
|
|
358
|
-
// Test (startup-window): resolver === null, resolveNativeCapOwnerSync returns a hub-local
|
|
359
|
-
// owner — must return null (no Moleculer proxy to a UDS-only child)
|
|
360
|
-
// ---------------------------------------------------------------------------
|
|
361
|
-
|
|
362
|
-
describe('G2 — (startup-window) resolver null, resolveNativeCapOwnerSync returns hub-local owner: returns null', () => {
|
|
363
|
-
it('returns null and does NOT call buildNativeCapProxy when resolver is null and owner is hub-local', () => {
|
|
364
|
-
// resolver === null simulates the window before the CapRouteResolver is initialised
|
|
365
|
-
const resolveNativeCapOwnerSync = vi.fn().mockReturnValue({
|
|
366
|
-
addonId: 'addon-provider-x',
|
|
367
|
-
nodeId: `${HUB_NODE_ID}/provider-x`, // hub-local UDS child — must NOT get a Moleculer proxy
|
|
368
|
-
})
|
|
369
|
-
const buildNativeCapProxy = vi.fn()
|
|
370
|
-
|
|
371
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
372
|
-
resolver: null,
|
|
373
|
-
hubNodeId: HUB_NODE_ID,
|
|
374
|
-
resolveNativeCapOwnerSync,
|
|
375
|
-
buildNativeCapProxy,
|
|
376
|
-
})
|
|
377
|
-
|
|
378
|
-
const result = fallback('ptz', 7)
|
|
379
|
-
|
|
380
|
-
// Guard: hub-local UDS children must not receive a Moleculer proxy
|
|
381
|
-
expect(result).toBeNull()
|
|
382
|
-
expect(buildNativeCapProxy).not.toHaveBeenCalled()
|
|
383
|
-
// resolveNativeCapOwnerSync IS consulted (resolver skipped due to null)
|
|
384
|
-
expect(resolveNativeCapOwnerSync).toHaveBeenCalledWith('ptz', 7)
|
|
385
|
-
})
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
// ---------------------------------------------------------------------------
|
|
389
|
-
// Test: singleton (hub-in-process) cap resolves in-process, no resolveNativeCapOwnerSync
|
|
390
|
-
// ---------------------------------------------------------------------------
|
|
391
|
-
|
|
392
|
-
describe('G2 — (d) hub-in-process singleton: resolver classifies hub-in-process, resolveNativeCapOwnerSync not called', () => {
|
|
393
|
-
it('resolves hub-in-process singleton without consulting resolveNativeCapOwnerSync', () => {
|
|
394
|
-
const invokeSpy = vi.fn().mockResolvedValue({ ok: true })
|
|
395
|
-
const inProcessProviders = (_cap: string) =>
|
|
396
|
-
_cap === 'device-manager' ? { invoke: invokeSpy } : null
|
|
397
|
-
|
|
398
|
-
const deps: CapRouteResolverDeps = {
|
|
399
|
-
hubNodeId: HUB_NODE_ID,
|
|
400
|
-
broker: { call: vi.fn(), waitForServices: vi.fn() },
|
|
401
|
-
hubLocalRegistry: null,
|
|
402
|
-
nodeAuthority: makeNodeAuthority(new Map(), new Set([]), []),
|
|
403
|
-
inProcessProviders,
|
|
404
|
-
}
|
|
405
|
-
const resolver = new CapRouteResolver(deps)
|
|
406
|
-
|
|
407
|
-
const resolveNativeCapOwnerSync = vi.fn()
|
|
408
|
-
const buildNativeCapProxy = vi.fn()
|
|
409
|
-
|
|
410
|
-
const fallback = buildConsolidatedNativeFallback({
|
|
411
|
-
resolver,
|
|
412
|
-
hubNodeId: HUB_NODE_ID,
|
|
413
|
-
resolveNativeCapOwnerSync,
|
|
414
|
-
buildNativeCapProxy,
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
// For in-process singletons, the fallback is not called at all (CapabilityRegistry
|
|
418
|
-
// finds the provider directly). But if called with a deviceId for such a cap, the
|
|
419
|
-
// resolver will classify hub-in-process — not hub-local-uds — so we fall through
|
|
420
|
-
// to the remote branch and resolveNativeCapOwnerSync is consulted (returns null).
|
|
421
|
-
// This is the correct behavior: in-process singletons go through getNativeProvider
|
|
422
|
-
// on the local nativeProviders map, not through setNativeFallback.
|
|
423
|
-
//
|
|
424
|
-
// The consolidated fallback is specifically for CROSS-PROCESS native caps.
|
|
425
|
-
// The assertion here is that for a cap with NO hub-local-uds route AND no
|
|
426
|
-
// resolveNativeCapOwnerSync result, the fallback correctly returns null.
|
|
427
|
-
const result = fallback('device-manager', 7)
|
|
428
|
-
expect(result).toBeNull()
|
|
429
|
-
expect(buildNativeCapProxy).not.toHaveBeenCalled()
|
|
430
|
-
})
|
|
431
|
-
})
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cap-providers-location-import.spec.ts
|
|
3
|
-
*
|
|
4
|
-
* Verifies that `buildIntegrationsProvider().getAvailableTypes()` correctly
|
|
5
|
-
* surfaces the `supportsLocationImport` flag:
|
|
6
|
-
* - A `device-adoption` addon whose manifest declares `supportsLocationImport: true`
|
|
7
|
-
* → returned entry has `supportsLocationImport === true`.
|
|
8
|
-
* - A `device-adoption` addon WITHOUT the flag
|
|
9
|
-
* → returned entry has `supportsLocationImport === false`.
|
|
10
|
-
* - A `device-provider` addon (non-adoption kind), regardless of the flag
|
|
11
|
-
* → returned entry has `supportsLocationImport === false`.
|
|
12
|
-
*
|
|
13
|
-
* Harness mirrors integrations-delete-cascade.spec.ts in the same directory.
|
|
14
|
-
*/
|
|
15
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
16
|
-
import { buildIntegrationsProvider } from '../../api/core/cap-providers.js'
|
|
17
|
-
|
|
18
|
-
// ── Minimal stubs ────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
function makeIntegrationRegistry() {
|
|
21
|
-
return {
|
|
22
|
-
listIntegrations: vi.fn(async () => []),
|
|
23
|
-
getIntegration: vi.fn(async (_id: string) => null),
|
|
24
|
-
deleteIntegration: vi.fn(async (_id: string) => undefined),
|
|
25
|
-
createIntegration: vi.fn(),
|
|
26
|
-
updateIntegration: vi.fn(),
|
|
27
|
-
getIntegrationByAddonId: vi.fn(),
|
|
28
|
-
getIntegrationSettings: vi.fn(),
|
|
29
|
-
setIntegrationSettings: vi.fn(),
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type FakeAddonRow = {
|
|
34
|
-
manifest: {
|
|
35
|
-
id: string
|
|
36
|
-
name: string
|
|
37
|
-
description: string
|
|
38
|
-
capabilities: Array<{ name: string }>
|
|
39
|
-
supportsLocationImport?: boolean
|
|
40
|
-
brokerKind?: string
|
|
41
|
-
instanceMode?: string
|
|
42
|
-
icon?: string
|
|
43
|
-
color?: string
|
|
44
|
-
}
|
|
45
|
-
declaration?: {
|
|
46
|
-
supportsLocationImport?: boolean
|
|
47
|
-
brokerKind?: string
|
|
48
|
-
instanceMode?: string
|
|
49
|
-
icon?: string
|
|
50
|
-
color?: string
|
|
51
|
-
}
|
|
52
|
-
process?: { state: string }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function makeAddonRegistry(addons: FakeAddonRow[] = []) {
|
|
56
|
-
const integrationReg = makeIntegrationRegistry()
|
|
57
|
-
return {
|
|
58
|
-
getIntegrationRegistry: vi.fn(() => integrationReg),
|
|
59
|
-
listAddons: vi.fn(() => addons),
|
|
60
|
-
restartAddon: vi.fn(),
|
|
61
|
-
getCapabilityRegistry: vi.fn(() => ({
|
|
62
|
-
getProviderByAddon: vi.fn(() => null),
|
|
63
|
-
})),
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function makeEventBus() {
|
|
68
|
-
return { emit: vi.fn() }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function makeLogger() {
|
|
72
|
-
return {
|
|
73
|
-
info: vi.fn(),
|
|
74
|
-
warn: vi.fn(),
|
|
75
|
-
error: vi.fn(),
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function makeLoggingService() {
|
|
80
|
-
const log = makeLogger()
|
|
81
|
-
return { createLogger: vi.fn(() => log) }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
85
|
-
|
|
86
|
-
describe('getAvailableTypes — supportsLocationImport flag', () => {
|
|
87
|
-
it('returns supportsLocationImport=true for a device-adoption addon that declares it in the manifest', async () => {
|
|
88
|
-
const addon: FakeAddonRow = {
|
|
89
|
-
manifest: {
|
|
90
|
-
id: 'provider-homeassistant',
|
|
91
|
-
name: 'Home Assistant Provider',
|
|
92
|
-
description: 'Adopt HA devices',
|
|
93
|
-
capabilities: [{ name: 'device-adoption' }],
|
|
94
|
-
supportsLocationImport: true,
|
|
95
|
-
brokerKind: 'home-assistant',
|
|
96
|
-
instanceMode: 'multiple',
|
|
97
|
-
},
|
|
98
|
-
}
|
|
99
|
-
const ar = makeAddonRegistry([addon])
|
|
100
|
-
const provider = buildIntegrationsProvider(
|
|
101
|
-
ar as never,
|
|
102
|
-
makeEventBus() as never,
|
|
103
|
-
makeLoggingService() as never,
|
|
104
|
-
null,
|
|
105
|
-
)
|
|
106
|
-
const types = await provider.getAvailableTypes()
|
|
107
|
-
|
|
108
|
-
expect(types).toHaveLength(1)
|
|
109
|
-
expect(types[0]).toMatchObject({
|
|
110
|
-
addonId: 'provider-homeassistant',
|
|
111
|
-
kind: 'device-adoption',
|
|
112
|
-
supportsLocationImport: true,
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('returns supportsLocationImport=false for a device-adoption addon that omits the flag', async () => {
|
|
117
|
-
const addon: FakeAddonRow = {
|
|
118
|
-
manifest: {
|
|
119
|
-
id: 'provider-test-adoption',
|
|
120
|
-
name: 'Test Adoption Provider',
|
|
121
|
-
description: 'Adoption without location import',
|
|
122
|
-
capabilities: [{ name: 'device-adoption' }],
|
|
123
|
-
brokerKind: 'test-broker',
|
|
124
|
-
instanceMode: 'multiple',
|
|
125
|
-
},
|
|
126
|
-
}
|
|
127
|
-
const ar = makeAddonRegistry([addon])
|
|
128
|
-
const provider = buildIntegrationsProvider(
|
|
129
|
-
ar as never,
|
|
130
|
-
makeEventBus() as never,
|
|
131
|
-
makeLoggingService() as never,
|
|
132
|
-
null,
|
|
133
|
-
)
|
|
134
|
-
const types = await provider.getAvailableTypes()
|
|
135
|
-
|
|
136
|
-
expect(types).toHaveLength(1)
|
|
137
|
-
expect(types[0]).toMatchObject({
|
|
138
|
-
addonId: 'provider-test-adoption',
|
|
139
|
-
kind: 'device-adoption',
|
|
140
|
-
supportsLocationImport: false,
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('returns supportsLocationImport=false for a device-provider addon even when the flag is set on manifest', async () => {
|
|
145
|
-
const addon: FakeAddonRow = {
|
|
146
|
-
manifest: {
|
|
147
|
-
id: 'provider-reolink',
|
|
148
|
-
name: 'Reolink Provider',
|
|
149
|
-
description: 'Classic device provider',
|
|
150
|
-
capabilities: [{ name: 'device-provider' }],
|
|
151
|
-
// Hypothetical: even if someone mistakenly sets this on a device-provider
|
|
152
|
-
supportsLocationImport: true,
|
|
153
|
-
instanceMode: 'single',
|
|
154
|
-
},
|
|
155
|
-
}
|
|
156
|
-
const ar = makeAddonRegistry([addon])
|
|
157
|
-
const provider = buildIntegrationsProvider(
|
|
158
|
-
ar as never,
|
|
159
|
-
makeEventBus() as never,
|
|
160
|
-
makeLoggingService() as never,
|
|
161
|
-
null,
|
|
162
|
-
)
|
|
163
|
-
const types = await provider.getAvailableTypes()
|
|
164
|
-
|
|
165
|
-
expect(types).toHaveLength(1)
|
|
166
|
-
expect(types[0]).toMatchObject({
|
|
167
|
-
addonId: 'provider-reolink',
|
|
168
|
-
kind: 'device-provider',
|
|
169
|
-
supportsLocationImport: false,
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('prefers declaration.supportsLocationImport over manifest when declaration is present', async () => {
|
|
174
|
-
const addon: FakeAddonRow = {
|
|
175
|
-
manifest: {
|
|
176
|
-
id: 'provider-homeassistant',
|
|
177
|
-
name: 'Home Assistant Provider',
|
|
178
|
-
description: 'Adopt HA devices',
|
|
179
|
-
capabilities: [{ name: 'device-adoption' }],
|
|
180
|
-
supportsLocationImport: false,
|
|
181
|
-
brokerKind: 'home-assistant',
|
|
182
|
-
instanceMode: 'multiple',
|
|
183
|
-
},
|
|
184
|
-
declaration: {
|
|
185
|
-
supportsLocationImport: true,
|
|
186
|
-
brokerKind: 'home-assistant',
|
|
187
|
-
instanceMode: 'multiple',
|
|
188
|
-
},
|
|
189
|
-
}
|
|
190
|
-
const ar = makeAddonRegistry([addon])
|
|
191
|
-
const provider = buildIntegrationsProvider(
|
|
192
|
-
ar as never,
|
|
193
|
-
makeEventBus() as never,
|
|
194
|
-
makeLoggingService() as never,
|
|
195
|
-
null,
|
|
196
|
-
)
|
|
197
|
-
const types = await provider.getAvailableTypes()
|
|
198
|
-
|
|
199
|
-
expect(types).toHaveLength(1)
|
|
200
|
-
expect(types[0]).toMatchObject({
|
|
201
|
-
addonId: 'provider-homeassistant',
|
|
202
|
-
kind: 'device-adoption',
|
|
203
|
-
supportsLocationImport: true,
|
|
204
|
-
})
|
|
205
|
-
})
|
|
206
|
-
})
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
-
import { __resetCapUsageRegistryForTests, getCapUsageRegistry } from '@camstack/kernel'
|
|
3
|
-
import { buildNodesProvider } from '../../api/core/cap-providers'
|
|
4
|
-
|
|
5
|
-
describe('nodes.getCapUsageGraph provider', () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
__resetCapUsageRegistryForTests()
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('returns recorded edges projected through the cap-usage registry', async () => {
|
|
11
|
-
const reg = getCapUsageRegistry()
|
|
12
|
-
const t0 = Date.now() - 5_000
|
|
13
|
-
reg.recordCall({
|
|
14
|
-
callerAddonId: 'A',
|
|
15
|
-
providerAddonId: 'B',
|
|
16
|
-
capName: 'foo',
|
|
17
|
-
methodName: 'm',
|
|
18
|
-
atMs: t0,
|
|
19
|
-
})
|
|
20
|
-
reg.recordCall({
|
|
21
|
-
callerAddonId: 'A',
|
|
22
|
-
providerAddonId: 'B',
|
|
23
|
-
capName: 'foo',
|
|
24
|
-
methodName: 'm',
|
|
25
|
-
atMs: t0 + 1000,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
const provider = buildNodesProvider({} as any, { broker: { call: vi.fn() } } as any)
|
|
30
|
-
const out = await provider.getCapUsageGraph({ windowSeconds: 60 })
|
|
31
|
-
expect(out).toHaveLength(1)
|
|
32
|
-
expect(out[0]!.callerAddonId).toBe('A')
|
|
33
|
-
expect(out[0]!.providerAddonId).toBe('B')
|
|
34
|
-
expect(out[0]!.capName).toBe('foo')
|
|
35
|
-
expect(out[0]!.callsPerMin).toBeGreaterThan(0)
|
|
36
|
-
})
|
|
37
|
-
})
|