@docker-harpoon/core 0.1.3 → 0.1.5
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/dist/__tests__/bindings.test.js +9 -5
- package/dist/__tests__/container.test.js +30 -9
- package/dist/__tests__/database.test.js +0 -14
- package/dist/__tests__/docker-infra.template.d.ts +2 -0
- package/dist/__tests__/docker-infra.template.d.ts.map +1 -0
- package/dist/__tests__/docker-infra.template.js +174 -0
- package/dist/__tests__/test-setup.d.ts +9 -0
- package/dist/__tests__/test-setup.d.ts.map +1 -0
- package/dist/__tests__/test-setup.js +27 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/promise.d.ts +13 -3
- package/dist/api/promise.d.ts.map +1 -1
- package/dist/api/promise.js +33 -18
- package/dist/bindings/index.d.ts +2 -2
- package/dist/bindings/index.d.ts.map +1 -1
- package/dist/bindings/index.js +1 -1
- package/dist/bindings/types.d.ts.map +1 -1
- package/dist/bindings/types.js +1 -3
- package/dist/build-strategies/types.d.ts.map +1 -1
- package/dist/config-patchers/index.d.ts.map +1 -1
- package/dist/config-patchers/types.d.ts.map +1 -1
- package/dist/dockerfile-transformers/core.d.ts.map +1 -1
- package/dist/dockerfile-transformers/core.js +2 -5
- package/dist/dockerfile-transformers/index.d.ts.map +1 -1
- package/dist/dockerfile-transformers/types.d.ts.map +1 -1
- package/dist/errors.d.ts +6 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -3
- package/dist/helpers/database.d.ts.map +1 -1
- package/dist/helpers/database.js +1 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/resources/container.d.ts +25 -2
- package/dist/resources/container.d.ts.map +1 -1
- package/dist/resources/container.js +301 -77
- package/dist/resources/image.d.ts.map +1 -1
- package/dist/resources/image.js +14 -35
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/network.d.ts.map +1 -1
- package/dist/resources/network.js +23 -9
- package/dist/resources/schemas.d.ts +178 -18
- package/dist/resources/schemas.d.ts.map +1 -1
- package/dist/resources/schemas.js +2 -1
- package/dist/services/CircuitBreaker.d.ts +83 -0
- package/dist/services/CircuitBreaker.d.ts.map +1 -0
- package/dist/services/CircuitBreaker.js +164 -0
- package/dist/services/ContainerPool.d.ts +82 -0
- package/dist/services/ContainerPool.d.ts.map +1 -0
- package/dist/services/ContainerPool.js +186 -0
- package/dist/services/DockerBatcher.d.ts +74 -0
- package/dist/services/DockerBatcher.d.ts.map +1 -0
- package/dist/services/DockerBatcher.js +107 -0
- package/dist/services/DockerClient.d.ts +125 -0
- package/dist/services/DockerClient.d.ts.map +1 -0
- package/dist/services/DockerClient.js +220 -0
- package/dist/services/DockerErrors.d.ts +145 -0
- package/dist/services/DockerErrors.d.ts.map +1 -0
- package/dist/services/DockerErrors.js +224 -0
- package/dist/services/DockerRateLimiter.d.ts +80 -0
- package/dist/services/DockerRateLimiter.d.ts.map +1 -0
- package/dist/services/DockerRateLimiter.js +93 -0
- package/dist/services/EventBus.d.ts +126 -0
- package/dist/services/EventBus.d.ts.map +1 -0
- package/dist/services/EventBus.js +111 -0
- package/dist/services/Harpoon.d.ts +151 -0
- package/dist/services/Harpoon.d.ts.map +1 -0
- package/dist/services/Harpoon.js +148 -0
- package/dist/services/HarpoonConfig.d.ts +60 -0
- package/dist/services/HarpoonConfig.d.ts.map +1 -0
- package/dist/services/HarpoonConfig.js +67 -0
- package/dist/services/HarpoonLogger.d.ts +36 -0
- package/dist/services/HarpoonLogger.d.ts.map +1 -0
- package/dist/services/HarpoonLogger.js +94 -0
- package/dist/services/ReadinessCoordinator.d.ts +128 -0
- package/dist/services/ReadinessCoordinator.d.ts.map +1 -0
- package/dist/services/ReadinessCoordinator.js +170 -0
- package/dist/services/ResourceTracker.d.ts +74 -0
- package/dist/services/ResourceTracker.d.ts.map +1 -0
- package/dist/services/ResourceTracker.js +145 -0
- package/dist/services/index.d.ts +29 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +47 -0
- package/dist/testing/helpers.d.ts +114 -0
- package/dist/testing/helpers.d.ts.map +1 -0
- package/dist/testing/helpers.js +140 -0
- package/dist/testing/index.d.ts +29 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +47 -0
- package/dist/testing/mocks.d.ts +66 -0
- package/dist/testing/mocks.d.ts.map +1 -0
- package/dist/testing/mocks.js +224 -0
- package/dist/utils/process.d.ts +24 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +49 -0
- package/package.json +12 -8
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResourceTracker Service
|
|
3
|
+
*
|
|
4
|
+
* Tracks all created resources (containers, networks, etc.) for lifecycle management.
|
|
5
|
+
* Uses Effect's Ref for thread-safe state management.
|
|
6
|
+
*/
|
|
7
|
+
import { Context, Effect, Layer, Ref, Option, Duration } from 'effect';
|
|
8
|
+
import { HarpoonConfig } from './HarpoonConfig';
|
|
9
|
+
import { cleanupRetrySchedule } from './DockerRateLimiter';
|
|
10
|
+
/**
|
|
11
|
+
* ResourceTracker service tag for dependency injection.
|
|
12
|
+
*/
|
|
13
|
+
export class ResourceTracker extends Context.Tag('@harpoon/ResourceTracker')() {
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a cleanup effect with retry and timeout for a single resource.
|
|
17
|
+
*/
|
|
18
|
+
const makeCleanupWithRetry = (resource, timeout) => resource.cleanup().pipe(
|
|
19
|
+
// Add per-resource timeout
|
|
20
|
+
Effect.timeoutFail({
|
|
21
|
+
duration: timeout,
|
|
22
|
+
onTimeout: () => new Error('Cleanup timed out'),
|
|
23
|
+
}),
|
|
24
|
+
// Retry on transient failures
|
|
25
|
+
Effect.retry({
|
|
26
|
+
schedule: cleanupRetrySchedule,
|
|
27
|
+
}),
|
|
28
|
+
// Log success
|
|
29
|
+
Effect.tap(() => Effect.logDebug('Resource destroyed').pipe(Effect.annotateLogs({
|
|
30
|
+
resourceId: resource.id,
|
|
31
|
+
resourceType: resource.type,
|
|
32
|
+
}))),
|
|
33
|
+
// Catch all errors - log but don't fail the overall cleanup
|
|
34
|
+
Effect.catchAll((error) => Effect.logWarning(`Failed to destroy resource: ${error}`).pipe(Effect.annotateLogs({
|
|
35
|
+
resourceId: resource.id,
|
|
36
|
+
resourceType: resource.type,
|
|
37
|
+
timeoutMs: Duration.toMillis(timeout),
|
|
38
|
+
}))));
|
|
39
|
+
/**
|
|
40
|
+
* Live implementation using Effect Ref for state management.
|
|
41
|
+
* Enhanced with retry schedules and per-resource timeouts.
|
|
42
|
+
*/
|
|
43
|
+
export const ResourceTrackerLive = Layer.scoped(ResourceTracker, Effect.gen(function* () {
|
|
44
|
+
const config = yield* HarpoonConfig;
|
|
45
|
+
// Thread-safe state using Ref
|
|
46
|
+
const resourcesRef = yield* Ref.make(new Map());
|
|
47
|
+
const service = {
|
|
48
|
+
_tag: 'ResourceTracker',
|
|
49
|
+
register: (resource) => Ref.update(resourcesRef, (map) => {
|
|
50
|
+
const newMap = new Map(map);
|
|
51
|
+
newMap.set(resource.id, resource);
|
|
52
|
+
return newMap;
|
|
53
|
+
}).pipe(Effect.tap(() => Effect.logDebug('Resource registered').pipe(Effect.annotateLogs({
|
|
54
|
+
resourceId: resource.id,
|
|
55
|
+
resourceType: resource.type,
|
|
56
|
+
resourceName: resource.name,
|
|
57
|
+
})))),
|
|
58
|
+
unregister: (id) => Ref.update(resourcesRef, (map) => {
|
|
59
|
+
const newMap = new Map(map);
|
|
60
|
+
newMap.delete(id);
|
|
61
|
+
return newMap;
|
|
62
|
+
}).pipe(Effect.tap(() => Effect.logDebug('Resource unregistered').pipe(Effect.annotateLogs({ resourceId: id })))),
|
|
63
|
+
get: (id) => Ref.get(resourcesRef).pipe(Effect.map((map) => Option.fromNullable(map.get(id)))),
|
|
64
|
+
getAll: () => Ref.get(resourcesRef).pipe(Effect.map((map) => [...map.values()])),
|
|
65
|
+
getByType: (type) => Ref.get(resourcesRef).pipe(Effect.map((map) => [...map.values()].filter((r) => r.type === type))),
|
|
66
|
+
count: () => Ref.get(resourcesRef).pipe(Effect.map((map) => map.size)),
|
|
67
|
+
destroyAll: (options) => Effect.gen(function* () {
|
|
68
|
+
const resources = yield* Ref.get(resourcesRef);
|
|
69
|
+
const resourceList = [...resources.values()];
|
|
70
|
+
if (resourceList.length === 0) {
|
|
71
|
+
yield* Effect.logDebug('No resources to destroy');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
yield* Effect.logInfo(`Destroying ${resourceList.length} resources`);
|
|
75
|
+
// Sort by priority (descending) - higher priority cleaned up first
|
|
76
|
+
const sorted = resourceList.sort((a, b) => b.priority - a.priority);
|
|
77
|
+
// Execute cleanups with concurrency control
|
|
78
|
+
const concurrency = options?.concurrency ?? 4;
|
|
79
|
+
// Use mode: 'either' to continue on failures
|
|
80
|
+
yield* Effect.all(sorted.map((resource) => makeCleanupWithRetry(resource, config.cleanupTimeout)), { concurrency, mode: 'either' });
|
|
81
|
+
// Clear all tracking
|
|
82
|
+
yield* Ref.set(resourcesRef, new Map());
|
|
83
|
+
yield* Effect.logInfo('All resources destroyed');
|
|
84
|
+
}),
|
|
85
|
+
destroyByType: (type, options) => Effect.gen(function* () {
|
|
86
|
+
const resources = yield* Ref.get(resourcesRef);
|
|
87
|
+
const typeResources = [...resources.values()].filter((r) => r.type === type);
|
|
88
|
+
if (typeResources.length === 0) {
|
|
89
|
+
yield* Effect.logDebug(`No ${type} resources to destroy`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
yield* Effect.logInfo(`Destroying ${typeResources.length} ${type} resources`);
|
|
93
|
+
// Sort by priority
|
|
94
|
+
const sorted = typeResources.sort((a, b) => b.priority - a.priority);
|
|
95
|
+
const concurrency = options?.concurrency ?? 4;
|
|
96
|
+
// Use mode: 'either' to continue on failures
|
|
97
|
+
yield* Effect.all(sorted.map((resource) => makeCleanupWithRetry(resource, config.cleanupTimeout).pipe(Effect.tap(() => Ref.update(resourcesRef, (map) => {
|
|
98
|
+
const newMap = new Map(map);
|
|
99
|
+
newMap.delete(resource.id);
|
|
100
|
+
return newMap;
|
|
101
|
+
})))), { concurrency, mode: 'either' });
|
|
102
|
+
yield* Effect.logInfo(`All ${type} resources destroyed`);
|
|
103
|
+
}),
|
|
104
|
+
clear: () => Ref.set(resourcesRef, new Map()).pipe(Effect.tap(() => Effect.logWarning('Resource tracking cleared'))),
|
|
105
|
+
};
|
|
106
|
+
// Register cleanup on scope close
|
|
107
|
+
yield* Effect.addFinalizer(() => service
|
|
108
|
+
.destroyAll()
|
|
109
|
+
.pipe(Effect.catchAll((e) => Effect.logError(`Finalizer cleanup failed: ${e}`))));
|
|
110
|
+
return service;
|
|
111
|
+
}));
|
|
112
|
+
/**
|
|
113
|
+
* Test implementation that tracks resources in memory but doesn't actually clean up.
|
|
114
|
+
*/
|
|
115
|
+
export const ResourceTrackerTest = Layer.scoped(ResourceTracker, Effect.gen(function* () {
|
|
116
|
+
const resourcesRef = yield* Ref.make(new Map());
|
|
117
|
+
return {
|
|
118
|
+
_tag: 'ResourceTracker',
|
|
119
|
+
register: (resource) => Ref.update(resourcesRef, (map) => {
|
|
120
|
+
const newMap = new Map(map);
|
|
121
|
+
newMap.set(resource.id, resource);
|
|
122
|
+
return newMap;
|
|
123
|
+
}),
|
|
124
|
+
unregister: (id) => Ref.update(resourcesRef, (map) => {
|
|
125
|
+
const newMap = new Map(map);
|
|
126
|
+
newMap.delete(id);
|
|
127
|
+
return newMap;
|
|
128
|
+
}),
|
|
129
|
+
get: (id) => Ref.get(resourcesRef).pipe(Effect.map((map) => Option.fromNullable(map.get(id)))),
|
|
130
|
+
getAll: () => Ref.get(resourcesRef).pipe(Effect.map((map) => [...map.values()])),
|
|
131
|
+
getByType: (type) => Ref.get(resourcesRef).pipe(Effect.map((map) => [...map.values()].filter((r) => r.type === type))),
|
|
132
|
+
count: () => Ref.get(resourcesRef).pipe(Effect.map((map) => map.size)),
|
|
133
|
+
destroyAll: () => Ref.set(resourcesRef, new Map()).pipe(Effect.asVoid),
|
|
134
|
+
destroyByType: (type) => Ref.update(resourcesRef, (map) => {
|
|
135
|
+
const newMap = new Map(map);
|
|
136
|
+
for (const [id, resource] of map) {
|
|
137
|
+
if (resource.type === type) {
|
|
138
|
+
newMap.delete(id);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return newMap;
|
|
142
|
+
}).pipe(Effect.asVoid),
|
|
143
|
+
clear: () => Ref.set(resourcesRef, new Map()),
|
|
144
|
+
};
|
|
145
|
+
}));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harpoon Services
|
|
3
|
+
*
|
|
4
|
+
* This module exports all Harpoon services and their Live/Test layers.
|
|
5
|
+
* Import from here for a unified API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* HarpoonLive,
|
|
11
|
+
* DockerClient,
|
|
12
|
+
* ResourceTracker,
|
|
13
|
+
* EventBus,
|
|
14
|
+
* } from '@docker-harpoon/core/services';
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export { HarpoonConfig, type HarpoonConfigService, HarpoonConfigLive, HarpoonConfigTest, makeHarpoonConfigTest, } from './HarpoonConfig';
|
|
18
|
+
export { HarpoonLoggerLive, HarpoonLoggerJson, HarpoonLoggerTest, HarpoonLoggerMinWarn, HarpoonLoggerDebug, withSpan, withLogAnnotations, } from './HarpoonLogger';
|
|
19
|
+
export { DockerClient, type DockerClientService, type CreateContainerOptions, type CreateNetworkOptions, type ListContainersOptions, type ListNetworksOptions, DockerClientLive, DockerClientTest, makeDockerClientTest, } from './DockerClient';
|
|
20
|
+
export { DockerConnectionError, DockerApiError, DockerContainerError, DockerNetworkError, DockerImageError, DockerVolumeError, type DockerError, isDockerConnectionError, isDockerApiError, isDockerError, wrapAsDockerError, isTransientError, } from './DockerErrors';
|
|
21
|
+
export { DockerRateLimiter, type DockerRateLimiterService, DockerRateLimiterLive, DockerRateLimiterTest, makeDockerRateLimiterTest, dockerRetrySchedule, cleanupRetrySchedule, healthCheckSchedule, } from './DockerRateLimiter';
|
|
22
|
+
export { CircuitBreaker, type CircuitBreakerService, type CircuitBreakerConfig, type CircuitState, CircuitOpenError, CircuitBreakerLive, CircuitBreakerTest, makeCircuitBreaker, makeCircuitBreakerLayer, } from './CircuitBreaker';
|
|
23
|
+
export { ResourceTracker, type ResourceTrackerService, type TrackedResource, type ResourceType, ResourceTrackerLive, ResourceTrackerTest, } from './ResourceTracker';
|
|
24
|
+
export { EventBus, type EventBusService, type HarpoonEvent, type ContainerEvent, type ContainerEventType, type NetworkEvent, type NetworkEventType, type DatabaseEvent, containerEvent, networkEvent, databaseEvent, EventBusLive, EventBusTest, } from './EventBus';
|
|
25
|
+
export { ReadinessCoordinator, type ReadinessCoordinatorService, type HealthCheck, type LogHealthCheck, type HttpHealthCheck, type TcpHealthCheck, type ExecHealthCheck, type CustomHealthCheck, type WaitOptions, ReadinessError, ReadinessCoordinatorLive, ReadinessCoordinatorTest, } from './ReadinessCoordinator';
|
|
26
|
+
export { DockerBatcher, type DockerBatcherService, type GetContainerInfoRequest, type GetNetworkInfoRequest, type DockerBatchRequest, GetContainerInfo, GetNetworkInfo, DockerBatcherLive, DockerBatcherTest, } from './DockerBatcher';
|
|
27
|
+
export { ContainerPool, type ContainerPoolService, type ContainerPoolConfig, type PooledContainer, makeContainerPool, ContainerPoolLive, ContainerPoolTest, } from './ContainerPool';
|
|
28
|
+
export { CoreLive, InfrastructureLive, DockerLive, BatcherLive, ResourcesLive, EventsLive, ReadinessLive, ServicesLive, HarpoonLive, CoreTest, InfrastructureTest, DockerTest, BatcherTest, ResourcesTest, EventsTest, ReadinessTest, ServicesTest, HarpoonTest, type HarpoonServices, type HarpoonRequirements, } from './Harpoon';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,WAAW,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EAEzB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAElB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,cAAc,EACd,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAEL,QAAQ,EACR,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EAGX,QAAQ,EACR,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EAGX,KAAK,eAAe,EACpB,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harpoon Services
|
|
3
|
+
*
|
|
4
|
+
* This module exports all Harpoon services and their Live/Test layers.
|
|
5
|
+
* Import from here for a unified API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* HarpoonLive,
|
|
11
|
+
* DockerClient,
|
|
12
|
+
* ResourceTracker,
|
|
13
|
+
* EventBus,
|
|
14
|
+
* } from '@docker-harpoon/core/services';
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
// ============ Configuration ============
|
|
18
|
+
export { HarpoonConfig, HarpoonConfigLive, HarpoonConfigTest, makeHarpoonConfigTest, } from './HarpoonConfig';
|
|
19
|
+
export { HarpoonLoggerLive, HarpoonLoggerJson, HarpoonLoggerTest, HarpoonLoggerMinWarn, HarpoonLoggerDebug, withSpan, withLogAnnotations, } from './HarpoonLogger';
|
|
20
|
+
// ============ Docker Client ============
|
|
21
|
+
export { DockerClient, DockerClientLive, DockerClientTest, makeDockerClientTest, } from './DockerClient';
|
|
22
|
+
// ============ Docker Errors ============
|
|
23
|
+
export { DockerConnectionError, DockerApiError, DockerContainerError, DockerNetworkError, DockerImageError, DockerVolumeError, isDockerConnectionError, isDockerApiError, isDockerError, wrapAsDockerError, isTransientError, } from './DockerErrors';
|
|
24
|
+
// ============ Rate Limiting ============
|
|
25
|
+
export { DockerRateLimiter, DockerRateLimiterLive, DockerRateLimiterTest, makeDockerRateLimiterTest,
|
|
26
|
+
// Retry schedules
|
|
27
|
+
dockerRetrySchedule, cleanupRetrySchedule, healthCheckSchedule, } from './DockerRateLimiter';
|
|
28
|
+
// ============ Circuit Breaker ============
|
|
29
|
+
export { CircuitBreaker, CircuitOpenError, CircuitBreakerLive, CircuitBreakerTest, makeCircuitBreaker, makeCircuitBreakerLayer, } from './CircuitBreaker';
|
|
30
|
+
// ============ Resource Tracking ============
|
|
31
|
+
export { ResourceTracker, ResourceTrackerLive, ResourceTrackerTest, } from './ResourceTracker';
|
|
32
|
+
// ============ Event Bus ============
|
|
33
|
+
export { EventBus,
|
|
34
|
+
// Event constructors
|
|
35
|
+
containerEvent, networkEvent, databaseEvent, EventBusLive, EventBusTest, } from './EventBus';
|
|
36
|
+
// ============ Readiness Coordination ============
|
|
37
|
+
export { ReadinessCoordinator, ReadinessError, ReadinessCoordinatorLive, ReadinessCoordinatorTest, } from './ReadinessCoordinator';
|
|
38
|
+
// ============ Request Batching ============
|
|
39
|
+
export { DockerBatcher, GetContainerInfo, GetNetworkInfo, DockerBatcherLive, DockerBatcherTest, } from './DockerBatcher';
|
|
40
|
+
// ============ Container Pool ============
|
|
41
|
+
export { ContainerPool, makeContainerPool, ContainerPoolLive, ContainerPoolTest, } from './ContainerPool';
|
|
42
|
+
// ============ Unified Layers ============
|
|
43
|
+
export {
|
|
44
|
+
// Live layers
|
|
45
|
+
CoreLive, InfrastructureLive, DockerLive, BatcherLive, ResourcesLive, EventsLive, ReadinessLive, ServicesLive, HarpoonLive,
|
|
46
|
+
// Test layers
|
|
47
|
+
CoreTest, InfrastructureTest, DockerTest, BatcherTest, ResourcesTest, EventsTest, ReadinessTest, ServicesTest, HarpoonTest, } from './Harpoon';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Helpers and Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utility functions for testing Harpoon services with Effect.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Layer, Exit, Scope, Duration, Stream } from 'effect';
|
|
7
|
+
import { HarpoonConfig } from '../services/HarpoonConfig';
|
|
8
|
+
import { DockerClient } from '../services/DockerClient';
|
|
9
|
+
import { DockerRateLimiter } from '../services/DockerRateLimiter';
|
|
10
|
+
import { CircuitBreaker } from '../services/CircuitBreaker';
|
|
11
|
+
import { ResourceTracker } from '../services/ResourceTracker';
|
|
12
|
+
import { EventBus } from '../services/EventBus';
|
|
13
|
+
import { ReadinessCoordinator } from '../services/ReadinessCoordinator';
|
|
14
|
+
/**
|
|
15
|
+
* Combined test layer with all mock services.
|
|
16
|
+
*/
|
|
17
|
+
export declare const TestServices: Layer.Layer<CircuitBreaker | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker, never, never>;
|
|
18
|
+
/**
|
|
19
|
+
* Full test layer including DockerClient.
|
|
20
|
+
*/
|
|
21
|
+
export declare const FullTestLayer: Layer.Layer<CircuitBreaker | DockerClient | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker, never, never>;
|
|
22
|
+
/**
|
|
23
|
+
* Run an effect with test services and return the result.
|
|
24
|
+
*/
|
|
25
|
+
export declare const runTest: <A, E>(effect: Effect.Effect<A, E, CircuitBreaker | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker>) => Promise<A>;
|
|
26
|
+
/**
|
|
27
|
+
* Run an effect with full test services including DockerClient.
|
|
28
|
+
*/
|
|
29
|
+
export declare const runFullTest: <A, E>(effect: Effect.Effect<A, E, CircuitBreaker | DockerClient | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker>) => Promise<A>;
|
|
30
|
+
/**
|
|
31
|
+
* Run an effect with a scope for cleanup.
|
|
32
|
+
*/
|
|
33
|
+
export declare const runScopedTest: <A, E>(effect: Effect.Effect<A, E, CircuitBreaker | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker | Scope.Scope>) => Promise<A>;
|
|
34
|
+
/**
|
|
35
|
+
* Run an effect and expect it to fail.
|
|
36
|
+
*/
|
|
37
|
+
export declare const runTestExpectError: <A, E>(effect: Effect.Effect<A, E, CircuitBreaker | DockerRateLimiter | EventBus | HarpoonConfig | ReadinessCoordinator | ResourceTracker>) => Promise<E>;
|
|
38
|
+
/**
|
|
39
|
+
* Assert that an effect succeeds with a specific value.
|
|
40
|
+
*/
|
|
41
|
+
export declare const assertSuccess: <A, E>(effect: Effect.Effect<A, E, never>, expected: A) => Effect.Effect<void, E, never>;
|
|
42
|
+
/**
|
|
43
|
+
* Assert that an effect fails with a specific error tag.
|
|
44
|
+
*/
|
|
45
|
+
export declare const assertFailsWithTag: <A, E extends {
|
|
46
|
+
_tag: string;
|
|
47
|
+
}>(effect: Effect.Effect<A, E, never>, expectedTag: E["_tag"]) => Effect.Effect<void, never, never>;
|
|
48
|
+
/**
|
|
49
|
+
* Assert that an effect completes within a timeout.
|
|
50
|
+
*/
|
|
51
|
+
export declare const assertCompletesWithin: <A, E>(effect: Effect.Effect<A, E, never>, timeout: Duration.Duration) => Effect.Effect<A, E | Error, never>;
|
|
52
|
+
/**
|
|
53
|
+
* Generate a unique test ID.
|
|
54
|
+
*/
|
|
55
|
+
export declare const uniqueId: (prefix?: string) => string;
|
|
56
|
+
/**
|
|
57
|
+
* Generate mock container stats.
|
|
58
|
+
*/
|
|
59
|
+
export declare const mockContainerStats: (overrides?: Record<string, unknown>) => {
|
|
60
|
+
cpu: {
|
|
61
|
+
usage: number;
|
|
62
|
+
system: number;
|
|
63
|
+
percent: number;
|
|
64
|
+
cores: number;
|
|
65
|
+
};
|
|
66
|
+
memory: {
|
|
67
|
+
usage: number;
|
|
68
|
+
limit: number;
|
|
69
|
+
percent: number;
|
|
70
|
+
usageMB: number;
|
|
71
|
+
limitMB: number;
|
|
72
|
+
};
|
|
73
|
+
network: {
|
|
74
|
+
rxBytes: number;
|
|
75
|
+
txBytes: number;
|
|
76
|
+
rxPackets: number;
|
|
77
|
+
txPackets: number;
|
|
78
|
+
};
|
|
79
|
+
timestamp: string;
|
|
80
|
+
containerId: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Generate mock log lines.
|
|
84
|
+
*/
|
|
85
|
+
export declare const mockLogLines: (count: number, stream?: "stderr" | "stdout") => {
|
|
86
|
+
stream: "stderr" | "stdout";
|
|
87
|
+
message: string;
|
|
88
|
+
timestamp: string;
|
|
89
|
+
}[];
|
|
90
|
+
/**
|
|
91
|
+
* Create a test scope that auto-cleans.
|
|
92
|
+
*/
|
|
93
|
+
export declare const withTestScope: <A, E, R>(fn: (scope: Scope.Scope) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
94
|
+
/**
|
|
95
|
+
* Delay helper for testing async operations.
|
|
96
|
+
*/
|
|
97
|
+
export declare const testDelay: (ms: number) => Effect.Effect<void, never, never>;
|
|
98
|
+
/**
|
|
99
|
+
* Collect all values from a stream for testing.
|
|
100
|
+
*/
|
|
101
|
+
export declare const collectStream: <A, E>(streamEffect: Effect.Effect<Stream.Stream<A, E, never>, never, never>, maxItems?: number) => Effect.Effect<readonly A[], E, never>;
|
|
102
|
+
/**
|
|
103
|
+
* Check if an Exit is successful.
|
|
104
|
+
*/
|
|
105
|
+
export declare const isSuccess: <A, E>(exit: Exit.Exit<A, E>) => exit is Exit.Success<A, E>;
|
|
106
|
+
/**
|
|
107
|
+
* Check if an Exit is a failure.
|
|
108
|
+
*/
|
|
109
|
+
export declare const isFailure: <A, E>(exit: Exit.Exit<A, E>) => exit is Exit.Failure<A, E>;
|
|
110
|
+
/**
|
|
111
|
+
* Get the error from a failure Exit.
|
|
112
|
+
*/
|
|
113
|
+
export declare const getFailureError: <A, E>(exit: Exit.Exit<A, E>) => E | undefined;
|
|
114
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/testing/helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAkB,KAAK,EAAE,QAAQ,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE7F,OAAO,EAAE,aAAa,EAAqB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAoB,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAyB,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAE,cAAc,EAAsB,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,eAAe,EAAuB,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAgB,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAA4B,MAAM,kCAAkC,CAAC;AAIlG;;GAEG;AACH,eAAO,MAAM,YAAY,mIAOxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,kJAAiD,CAAC;AAI5E;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,CAAC,oJAWiD,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,CAAC,mKAY8C,CAAC;AAE/E;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,kKAY0D,CAAC;AAE7F;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,CAAC,oJAWmD,CAAC;AAI3F;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,mFAU/B,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,CAAC;;mGAapC,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAE,CAAC,uGASvC,CAAC;AAIJ;;GAEG;AACH,eAAO,MAAM,QAAQ,6BACoD,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;CAuB7B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY;;;;GAKpB,CAAC;AAIN;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,+EAGiD,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,SAAS,mDAAyE,CAAC;AAEhG;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,oIAS9B,CAAC;AAIL;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,sDACR,CAAC;AAEvB;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,sDACR,CAAC;AAEvB;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,yCAQnC,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Helpers and Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utility functions for testing Harpoon services with Effect.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Layer, Exit, Cause, Scope, Duration, Stream } from 'effect';
|
|
7
|
+
import { HarpoonConfigTest } from '../services/HarpoonConfig';
|
|
8
|
+
import { DockerClientTest } from '../services/DockerClient';
|
|
9
|
+
import { DockerRateLimiterTest } from '../services/DockerRateLimiter';
|
|
10
|
+
import { CircuitBreakerTest } from '../services/CircuitBreaker';
|
|
11
|
+
import { ResourceTrackerTest } from '../services/ResourceTracker';
|
|
12
|
+
import { EventBusTest } from '../services/EventBus';
|
|
13
|
+
import { ReadinessCoordinatorTest } from '../services/ReadinessCoordinator';
|
|
14
|
+
// ============ Test Layers ============
|
|
15
|
+
/**
|
|
16
|
+
* Combined test layer with all mock services.
|
|
17
|
+
*/
|
|
18
|
+
export const TestServices = Layer.mergeAll(HarpoonConfigTest, DockerRateLimiterTest, CircuitBreakerTest, ResourceTrackerTest, EventBusTest, ReadinessCoordinatorTest);
|
|
19
|
+
/**
|
|
20
|
+
* Full test layer including DockerClient.
|
|
21
|
+
*/
|
|
22
|
+
export const FullTestLayer = Layer.mergeAll(TestServices, DockerClientTest);
|
|
23
|
+
// ============ Test Runners ============
|
|
24
|
+
/**
|
|
25
|
+
* Run an effect with test services and return the result.
|
|
26
|
+
*/
|
|
27
|
+
export const runTest = (effect) => Effect.runPromise(effect.pipe(Effect.provide(TestServices)));
|
|
28
|
+
/**
|
|
29
|
+
* Run an effect with full test services including DockerClient.
|
|
30
|
+
*/
|
|
31
|
+
export const runFullTest = (effect) => Effect.runPromise(effect.pipe(Effect.provide(FullTestLayer)));
|
|
32
|
+
/**
|
|
33
|
+
* Run an effect with a scope for cleanup.
|
|
34
|
+
*/
|
|
35
|
+
export const runScopedTest = (effect) => Effect.runPromise(Effect.scoped(effect.pipe(Effect.provide(TestServices))));
|
|
36
|
+
/**
|
|
37
|
+
* Run an effect and expect it to fail.
|
|
38
|
+
*/
|
|
39
|
+
export const runTestExpectError = (effect) => Effect.runPromise(effect.pipe(Effect.provide(TestServices), Effect.flip));
|
|
40
|
+
// ============ Assertion Helpers ============
|
|
41
|
+
/**
|
|
42
|
+
* Assert that an effect succeeds with a specific value.
|
|
43
|
+
*/
|
|
44
|
+
export const assertSuccess = (effect, expected) => effect.pipe(Effect.flatMap((result) => result === expected
|
|
45
|
+
? Effect.void
|
|
46
|
+
: Effect.die(new Error(`Expected ${expected}, got ${result}`))));
|
|
47
|
+
/**
|
|
48
|
+
* Assert that an effect fails with a specific error tag.
|
|
49
|
+
*/
|
|
50
|
+
export const assertFailsWithTag = (effect, expectedTag) => effect.pipe(Effect.matchEffect({
|
|
51
|
+
onFailure: (error) => error._tag === expectedTag
|
|
52
|
+
? Effect.void
|
|
53
|
+
: Effect.die(new Error(`Expected error tag ${expectedTag}, got ${error._tag}`)),
|
|
54
|
+
onSuccess: () => Effect.die(new Error(`Expected failure with tag ${expectedTag}, but succeeded`)),
|
|
55
|
+
}));
|
|
56
|
+
/**
|
|
57
|
+
* Assert that an effect completes within a timeout.
|
|
58
|
+
*/
|
|
59
|
+
export const assertCompletesWithin = (effect, timeout) => effect.pipe(Effect.timeoutFail({
|
|
60
|
+
duration: timeout,
|
|
61
|
+
onTimeout: () => new Error(`Effect did not complete within ${Duration.toMillis(timeout)}ms`),
|
|
62
|
+
}));
|
|
63
|
+
// ============ Mock Data Generators ============
|
|
64
|
+
/**
|
|
65
|
+
* Generate a unique test ID.
|
|
66
|
+
*/
|
|
67
|
+
export const uniqueId = (prefix = 'test') => `${prefix}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
68
|
+
/**
|
|
69
|
+
* Generate mock container stats.
|
|
70
|
+
*/
|
|
71
|
+
export const mockContainerStats = (overrides = {}) => ({
|
|
72
|
+
cpu: {
|
|
73
|
+
usage: 1000000,
|
|
74
|
+
system: 100000000,
|
|
75
|
+
percent: 1.0,
|
|
76
|
+
cores: 4,
|
|
77
|
+
},
|
|
78
|
+
memory: {
|
|
79
|
+
usage: 104857600,
|
|
80
|
+
limit: 1073741824,
|
|
81
|
+
percent: 9.77,
|
|
82
|
+
usageMB: 100,
|
|
83
|
+
limitMB: 1024,
|
|
84
|
+
},
|
|
85
|
+
network: {
|
|
86
|
+
rxBytes: 1024,
|
|
87
|
+
txBytes: 2048,
|
|
88
|
+
rxPackets: 10,
|
|
89
|
+
txPackets: 20,
|
|
90
|
+
},
|
|
91
|
+
timestamp: new Date().toISOString(),
|
|
92
|
+
containerId: 'mock-container-id',
|
|
93
|
+
...overrides,
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Generate mock log lines.
|
|
97
|
+
*/
|
|
98
|
+
export const mockLogLines = (count, stream = 'stdout') => Array.from({ length: count }, (_, i) => ({
|
|
99
|
+
stream,
|
|
100
|
+
message: `Log line ${i + 1}`,
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
}));
|
|
103
|
+
// ============ Test Fixture Helpers ============
|
|
104
|
+
/**
|
|
105
|
+
* Create a test scope that auto-cleans.
|
|
106
|
+
*/
|
|
107
|
+
export const withTestScope = (fn) => Effect.acquireUseRelease(Scope.make(), fn, (scope) => Scope.close(scope, Exit.void));
|
|
108
|
+
/**
|
|
109
|
+
* Delay helper for testing async operations.
|
|
110
|
+
*/
|
|
111
|
+
export const testDelay = (ms) => Effect.sleep(Duration.millis(ms));
|
|
112
|
+
/**
|
|
113
|
+
* Collect all values from a stream for testing.
|
|
114
|
+
*/
|
|
115
|
+
export const collectStream = (streamEffect, maxItems = 100) => Effect.gen(function* () {
|
|
116
|
+
const s = yield* streamEffect;
|
|
117
|
+
const items = yield* s.pipe(Stream.take(maxItems), Stream.runCollect);
|
|
118
|
+
return Array.from(items);
|
|
119
|
+
});
|
|
120
|
+
// ============ Effect Test Matchers ============
|
|
121
|
+
/**
|
|
122
|
+
* Check if an Exit is successful.
|
|
123
|
+
*/
|
|
124
|
+
export const isSuccess = (exit) => Exit.isSuccess(exit);
|
|
125
|
+
/**
|
|
126
|
+
* Check if an Exit is a failure.
|
|
127
|
+
*/
|
|
128
|
+
export const isFailure = (exit) => Exit.isFailure(exit);
|
|
129
|
+
/**
|
|
130
|
+
* Get the error from a failure Exit.
|
|
131
|
+
*/
|
|
132
|
+
export const getFailureError = (exit) => {
|
|
133
|
+
if (Exit.isFailure(exit)) {
|
|
134
|
+
const cause = exit.cause;
|
|
135
|
+
if (Cause.isFailType(cause)) {
|
|
136
|
+
return cause.error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harpoon Testing Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations, test helpers, and utilities for testing
|
|
5
|
+
* Harpoon-based applications with Effect.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { runTest, createMockDockerClient, mockContainerStats } from '@docker-harpoon/core/testing';
|
|
10
|
+
*
|
|
11
|
+
* const test = Effect.gen(function* () {
|
|
12
|
+
* const container = yield* Container("test", { image: "alpine" });
|
|
13
|
+
* const stats = yield* container.stats();
|
|
14
|
+
* expect(stats.cpu.percent).toBeGreaterThan(0);
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* await runTest(test);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export { createMockContainer, createMockNetwork, type MockContainerConfig, createMockDockerClient, MockDockerRateLimiter, MockCircuitBreaker, MockResourceTracker, createMockResourceTracker, createMockEventBus, createMockReadinessCoordinator, createMockHarpoonConfig, MockHarpoonConfig, } from './mocks';
|
|
21
|
+
export { TestServices, FullTestLayer, runTest, runFullTest, runScopedTest, runTestExpectError, assertSuccess, assertFailsWithTag, assertCompletesWithin, uniqueId, mockContainerStats, mockLogLines, withTestScope, testDelay, collectStream, isSuccess, isFailure, getFailureError, } from './helpers';
|
|
22
|
+
export { HarpoonConfigTest, makeHarpoonConfigTest } from '../services/HarpoonConfig';
|
|
23
|
+
export { DockerClientTest, makeDockerClientTest } from '../services/DockerClient';
|
|
24
|
+
export { DockerRateLimiterTest, makeDockerRateLimiterTest } from '../services/DockerRateLimiter';
|
|
25
|
+
export { CircuitBreakerTest, makeCircuitBreakerLayer } from '../services/CircuitBreaker';
|
|
26
|
+
export { ResourceTrackerTest } from '../services/ResourceTracker';
|
|
27
|
+
export { EventBusTest } from '../services/EventBus';
|
|
28
|
+
export { ReadinessCoordinatorTest } from '../services/ReadinessCoordinator';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAEL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,mBAAmB,EAGxB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,8BAA8B,EAC9B,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAEL,YAAY,EACZ,aAAa,EAGb,OAAO,EACP,WAAW,EACX,aAAa,EACb,kBAAkB,EAGlB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EAGrB,QAAQ,EACR,kBAAkB,EAClB,YAAY,EAGZ,aAAa,EACb,SAAS,EACT,aAAa,EAGb,SAAS,EACT,SAAS,EACT,eAAe,GAChB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAElF,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAEjG,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAEzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harpoon Testing Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations, test helpers, and utilities for testing
|
|
5
|
+
* Harpoon-based applications with Effect.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { runTest, createMockDockerClient, mockContainerStats } from '@docker-harpoon/core/testing';
|
|
10
|
+
*
|
|
11
|
+
* const test = Effect.gen(function* () {
|
|
12
|
+
* const container = yield* Container("test", { image: "alpine" });
|
|
13
|
+
* const stats = yield* container.stats();
|
|
14
|
+
* expect(stats.cpu.percent).toBeGreaterThan(0);
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* await runTest(test);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
// Re-export all mocks
|
|
21
|
+
export {
|
|
22
|
+
// Mock container/network creation
|
|
23
|
+
createMockContainer, createMockNetwork,
|
|
24
|
+
// Mock service layers
|
|
25
|
+
createMockDockerClient, MockDockerRateLimiter, MockCircuitBreaker, MockResourceTracker, createMockResourceTracker, createMockEventBus, createMockReadinessCoordinator, createMockHarpoonConfig, MockHarpoonConfig, } from './mocks';
|
|
26
|
+
// Re-export all helpers
|
|
27
|
+
export {
|
|
28
|
+
// Test layers
|
|
29
|
+
TestServices, FullTestLayer,
|
|
30
|
+
// Test runners
|
|
31
|
+
runTest, runFullTest, runScopedTest, runTestExpectError,
|
|
32
|
+
// Assertions
|
|
33
|
+
assertSuccess, assertFailsWithTag, assertCompletesWithin,
|
|
34
|
+
// Mock data generators
|
|
35
|
+
uniqueId, mockContainerStats, mockLogLines,
|
|
36
|
+
// Test fixtures
|
|
37
|
+
withTestScope, testDelay, collectStream,
|
|
38
|
+
// Exit matchers
|
|
39
|
+
isSuccess, isFailure, getFailureError, } from './helpers';
|
|
40
|
+
// Re-export test layer implementations from services
|
|
41
|
+
export { HarpoonConfigTest, makeHarpoonConfigTest } from '../services/HarpoonConfig';
|
|
42
|
+
export { DockerClientTest, makeDockerClientTest } from '../services/DockerClient';
|
|
43
|
+
export { DockerRateLimiterTest, makeDockerRateLimiterTest } from '../services/DockerRateLimiter';
|
|
44
|
+
export { CircuitBreakerTest, makeCircuitBreakerLayer } from '../services/CircuitBreaker';
|
|
45
|
+
export { ResourceTrackerTest } from '../services/ResourceTracker';
|
|
46
|
+
export { EventBusTest } from '../services/EventBus';
|
|
47
|
+
export { ReadinessCoordinatorTest } from '../services/ReadinessCoordinator';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Implementations for Testing
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations of Harpoon services for unit testing.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Layer } from 'effect';
|
|
7
|
+
import Docker from 'dockerode';
|
|
8
|
+
import { DockerClient, type DockerClientService } from '../services/DockerClient';
|
|
9
|
+
import { DockerRateLimiter } from '../services/DockerRateLimiter';
|
|
10
|
+
import { CircuitBreaker } from '../services/CircuitBreaker';
|
|
11
|
+
import { ResourceTracker, type ResourceTrackerService } from '../services/ResourceTracker';
|
|
12
|
+
import { type EventBusService, type HarpoonEvent } from '../services/EventBus';
|
|
13
|
+
import { type ReadinessCoordinatorService } from '../services/ReadinessCoordinator';
|
|
14
|
+
import { HarpoonConfig, type HarpoonConfigService } from '../services/HarpoonConfig';
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for mock container behavior.
|
|
17
|
+
*/
|
|
18
|
+
export interface MockContainerConfig {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
running?: boolean;
|
|
22
|
+
logs?: string;
|
|
23
|
+
stats?: object;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a mock Docker container.
|
|
27
|
+
*/
|
|
28
|
+
export declare const createMockContainer: (config: MockContainerConfig) => Docker.Container;
|
|
29
|
+
/**
|
|
30
|
+
* Create a mock Docker network.
|
|
31
|
+
*/
|
|
32
|
+
export declare const createMockNetwork: (id: string, name?: string) => Docker.Network;
|
|
33
|
+
/**
|
|
34
|
+
* Create a mock DockerClient layer with customizable behavior.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createMockDockerClient: (overrides?: Partial<DockerClientService>) => Layer.Layer<DockerClient, never, never>;
|
|
37
|
+
/**
|
|
38
|
+
* Create a mock RateLimiter that doesn't limit.
|
|
39
|
+
*/
|
|
40
|
+
export declare const MockDockerRateLimiter: Layer.Layer<DockerRateLimiter>;
|
|
41
|
+
/**
|
|
42
|
+
* Create a mock CircuitBreaker that never opens.
|
|
43
|
+
*/
|
|
44
|
+
export declare const MockCircuitBreaker: Layer.Layer<CircuitBreaker>;
|
|
45
|
+
/**
|
|
46
|
+
* Create a mock ResourceTracker that tracks in memory.
|
|
47
|
+
*/
|
|
48
|
+
export declare const createMockResourceTracker: () => Effect.Effect<ResourceTrackerService, never, never>;
|
|
49
|
+
export declare const MockResourceTracker: Layer.Layer<ResourceTracker>;
|
|
50
|
+
/**
|
|
51
|
+
* Create a mock EventBus that collects events.
|
|
52
|
+
*/
|
|
53
|
+
export declare const createMockEventBus: () => Effect.Effect<{
|
|
54
|
+
service: EventBusService;
|
|
55
|
+
getEvents: () => Effect.Effect<readonly HarpoonEvent[], never, never>;
|
|
56
|
+
}, never, never>;
|
|
57
|
+
/**
|
|
58
|
+
* Create a mock ReadinessCoordinator.
|
|
59
|
+
*/
|
|
60
|
+
export declare const createMockReadinessCoordinator: (preReady?: string[]) => Effect.Effect<ReadinessCoordinatorService, never, never>;
|
|
61
|
+
/**
|
|
62
|
+
* Create a test configuration with fast timeouts.
|
|
63
|
+
*/
|
|
64
|
+
export declare const createMockHarpoonConfig: (overrides?: Partial<Omit<HarpoonConfigService, "_tag">>) => Layer.Layer<HarpoonConfig, never, never>;
|
|
65
|
+
export declare const MockHarpoonConfig: Layer.Layer<HarpoonConfig, never, never>;
|
|
66
|
+
//# sourceMappingURL=mocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mocks.d.ts","sourceRoot":"","sources":["../../src/testing/mocks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAgD,MAAM,QAAQ,CAAC;AACrF,OAAO,MAAM,MAAM,WAAW,CAAC;AAE/B,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EAGzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAuC,MAAM,4BAA4B,CAAC;AACjG,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAG5B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EAIlB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,2BAA2B,EAIjC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAIrF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,mDAuCG,CAAC;AAEpC;;GAEG;AACH,eAAO,MAAM,iBAAiB,+CAOG,CAAC;AAElC;;GAEG;AACH,eAAO,MAAM,sBAAsB,uFA8B/B,CAAC;AAIL;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAWhE,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAOzD,CAAC;AAIH;;GAEG;AACH,eAAO,MAAM,yBAAyB,2DA8BlC,CAAC;AAEL,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAAC,eAAe,CAG5D,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;gBAiD3B,CAAC;AAIL;;GAEG;AACH,eAAO,MAAM,8BAA8B,mFAkDvC,CAAC;AAIL;;GAEG;AACH,eAAO,MAAM,uBAAuB,uGAchC,CAAC;AAEL,eAAO,MAAM,iBAAiB,0CAA4B,CAAC"}
|