@hazeljs/discovery 0.2.0-beta.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.
Files changed (59) hide show
  1. package/README.md +281 -0
  2. package/dist/__tests__/decorators.test.d.ts +5 -0
  3. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  4. package/dist/__tests__/decorators.test.js +72 -0
  5. package/dist/__tests__/discovery-client.test.d.ts +5 -0
  6. package/dist/__tests__/discovery-client.test.d.ts.map +1 -0
  7. package/dist/__tests__/discovery-client.test.js +142 -0
  8. package/dist/__tests__/load-balancer-strategies.test.d.ts +5 -0
  9. package/dist/__tests__/load-balancer-strategies.test.d.ts.map +1 -0
  10. package/dist/__tests__/load-balancer-strategies.test.js +234 -0
  11. package/dist/__tests__/memory-backend.test.d.ts +5 -0
  12. package/dist/__tests__/memory-backend.test.d.ts.map +1 -0
  13. package/dist/__tests__/memory-backend.test.js +246 -0
  14. package/dist/__tests__/service-client.test.d.ts +5 -0
  15. package/dist/__tests__/service-client.test.d.ts.map +1 -0
  16. package/dist/__tests__/service-client.test.js +215 -0
  17. package/dist/__tests__/service-registry.test.d.ts +5 -0
  18. package/dist/__tests__/service-registry.test.d.ts.map +1 -0
  19. package/dist/__tests__/service-registry.test.js +65 -0
  20. package/dist/backends/consul-backend.d.ts +76 -0
  21. package/dist/backends/consul-backend.d.ts.map +1 -0
  22. package/dist/backends/consul-backend.js +275 -0
  23. package/dist/backends/kubernetes-backend.d.ts +65 -0
  24. package/dist/backends/kubernetes-backend.d.ts.map +1 -0
  25. package/dist/backends/kubernetes-backend.js +174 -0
  26. package/dist/backends/memory-backend.d.ts +22 -0
  27. package/dist/backends/memory-backend.d.ts.map +1 -0
  28. package/dist/backends/memory-backend.js +115 -0
  29. package/dist/backends/redis-backend.d.ts +71 -0
  30. package/dist/backends/redis-backend.d.ts.map +1 -0
  31. package/dist/backends/redis-backend.js +200 -0
  32. package/dist/backends/registry-backend.d.ts +39 -0
  33. package/dist/backends/registry-backend.d.ts.map +1 -0
  34. package/dist/backends/registry-backend.js +5 -0
  35. package/dist/client/discovery-client.d.ts +47 -0
  36. package/dist/client/discovery-client.d.ts.map +1 -0
  37. package/dist/client/discovery-client.js +123 -0
  38. package/dist/client/service-client.d.ts +52 -0
  39. package/dist/client/service-client.d.ts.map +1 -0
  40. package/dist/client/service-client.js +95 -0
  41. package/dist/decorators/inject-service-client.decorator.d.ts +16 -0
  42. package/dist/decorators/inject-service-client.decorator.d.ts.map +1 -0
  43. package/dist/decorators/inject-service-client.decorator.js +24 -0
  44. package/dist/decorators/service-registry.decorator.d.ts +11 -0
  45. package/dist/decorators/service-registry.decorator.d.ts.map +1 -0
  46. package/dist/decorators/service-registry.decorator.js +20 -0
  47. package/dist/index.d.ts +18 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +44 -0
  50. package/dist/load-balancer/strategies.d.ts +82 -0
  51. package/dist/load-balancer/strategies.d.ts.map +1 -0
  52. package/dist/load-balancer/strategies.js +209 -0
  53. package/dist/registry/service-registry.d.ts +51 -0
  54. package/dist/registry/service-registry.d.ts.map +1 -0
  55. package/dist/registry/service-registry.js +148 -0
  56. package/dist/types/index.d.ts +61 -0
  57. package/dist/types/index.d.ts.map +1 -0
  58. package/dist/types/index.js +14 -0
  59. package/package.json +78 -0
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ /**
3
+ * Load Balancer Strategies Tests
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strategies_1 = require("../load-balancer/strategies");
7
+ const types_1 = require("../types");
8
+ describe('Load Balancer Strategies', () => {
9
+ const createInstance = (id, status = types_1.ServiceStatus.UP) => ({
10
+ id,
11
+ name: 'test-service',
12
+ host: 'localhost',
13
+ port: 3000,
14
+ status,
15
+ lastHeartbeat: new Date(),
16
+ registeredAt: new Date(),
17
+ });
18
+ describe('RoundRobinStrategy', () => {
19
+ it('should cycle through instances in order', () => {
20
+ const strategy = new strategies_1.RoundRobinStrategy();
21
+ const instances = [createInstance('1'), createInstance('2'), createInstance('3')];
22
+ expect(strategy.choose(instances)?.id).toBe('1');
23
+ expect(strategy.choose(instances)?.id).toBe('2');
24
+ expect(strategy.choose(instances)?.id).toBe('3');
25
+ expect(strategy.choose(instances)?.id).toBe('1'); // Cycles back
26
+ });
27
+ it('should return null when no healthy instances', () => {
28
+ const strategy = new strategies_1.RoundRobinStrategy();
29
+ const instances = [
30
+ createInstance('1', types_1.ServiceStatus.DOWN),
31
+ createInstance('2', types_1.ServiceStatus.DOWN),
32
+ ];
33
+ expect(strategy.choose(instances)).toBeNull();
34
+ });
35
+ it('should filter out unhealthy instances', () => {
36
+ const strategy = new strategies_1.RoundRobinStrategy();
37
+ const instances = [
38
+ createInstance('1', types_1.ServiceStatus.UP),
39
+ createInstance('2', types_1.ServiceStatus.DOWN),
40
+ createInstance('3', types_1.ServiceStatus.UP),
41
+ ];
42
+ const first = strategy.choose(instances);
43
+ const second = strategy.choose(instances);
44
+ expect([first?.id, second?.id]).toEqual(['1', '3']);
45
+ });
46
+ });
47
+ describe('RandomStrategy', () => {
48
+ it('should return a random instance', () => {
49
+ const strategy = new strategies_1.RandomStrategy();
50
+ const instances = [createInstance('1'), createInstance('2'), createInstance('3')];
51
+ const selected = strategy.choose(instances);
52
+ expect(selected).toBeDefined();
53
+ expect(['1', '2', '3']).toContain(selected?.id);
54
+ });
55
+ it('should return null when no healthy instances', () => {
56
+ const strategy = new strategies_1.RandomStrategy();
57
+ const instances = [
58
+ createInstance('1', types_1.ServiceStatus.DOWN),
59
+ createInstance('2', types_1.ServiceStatus.DOWN),
60
+ ];
61
+ expect(strategy.choose(instances)).toBeNull();
62
+ });
63
+ });
64
+ describe('LeastConnectionsStrategy', () => {
65
+ it('should select instance with least connections', () => {
66
+ const strategy = new strategies_1.LeastConnectionsStrategy();
67
+ const instances = [createInstance('1'), createInstance('2'), createInstance('3')];
68
+ strategy.incrementConnections('1');
69
+ strategy.incrementConnections('1');
70
+ strategy.incrementConnections('2');
71
+ const selected = strategy.choose(instances);
72
+ expect(selected?.id).toBe('3');
73
+ });
74
+ it('should handle connection increments and decrements', () => {
75
+ const strategy = new strategies_1.LeastConnectionsStrategy();
76
+ const instances = [createInstance('1')];
77
+ strategy.incrementConnections('1');
78
+ strategy.incrementConnections('1');
79
+ expect(strategy.choose(instances)?.id).toBe('1');
80
+ strategy.decrementConnections('1');
81
+ strategy.decrementConnections('1');
82
+ expect(strategy.choose(instances)?.id).toBe('1');
83
+ });
84
+ it('should not allow negative connections', () => {
85
+ const strategy = new strategies_1.LeastConnectionsStrategy();
86
+ const instances = [createInstance('1')];
87
+ strategy.decrementConnections('1');
88
+ strategy.decrementConnections('1');
89
+ expect(strategy.choose(instances)?.id).toBe('1');
90
+ });
91
+ it('should return null when no healthy instances', () => {
92
+ const strategy = new strategies_1.LeastConnectionsStrategy();
93
+ const instances = [
94
+ createInstance('1', types_1.ServiceStatus.DOWN),
95
+ createInstance('2', types_1.ServiceStatus.DOWN),
96
+ ];
97
+ expect(strategy.choose(instances)).toBeNull();
98
+ });
99
+ });
100
+ describe('WeightedRoundRobinStrategy', () => {
101
+ it('should select instances based on weight', () => {
102
+ const strategy = new strategies_1.WeightedRoundRobinStrategy();
103
+ const instances = [
104
+ { ...createInstance('1'), metadata: { weight: 2 } },
105
+ { ...createInstance('2'), metadata: { weight: 1 } },
106
+ ];
107
+ const results = [];
108
+ for (let i = 0; i < 6; i++) {
109
+ const selected = strategy.choose(instances);
110
+ if (selected)
111
+ results.push(selected.id);
112
+ }
113
+ // Should have more '1' than '2' due to weight
114
+ const count1 = results.filter((id) => id === '1').length;
115
+ const count2 = results.filter((id) => id === '2').length;
116
+ expect(count1).toBeGreaterThan(count2);
117
+ });
118
+ it('should default to weight 1 when weight is missing', () => {
119
+ const strategy = new strategies_1.WeightedRoundRobinStrategy();
120
+ const instances = [createInstance('1'), createInstance('2')];
121
+ const selected = strategy.choose(instances);
122
+ expect(selected).toBeDefined();
123
+ });
124
+ it('should handle invalid weight values', () => {
125
+ const strategy = new strategies_1.WeightedRoundRobinStrategy();
126
+ const instances = [
127
+ { ...createInstance('1'), metadata: { weight: 'invalid' } },
128
+ { ...createInstance('2'), metadata: { weight: -1 } },
129
+ ];
130
+ const selected = strategy.choose(instances);
131
+ expect(selected).toBeDefined();
132
+ });
133
+ it('should return null when no healthy instances', () => {
134
+ const strategy = new strategies_1.WeightedRoundRobinStrategy();
135
+ const instances = [
136
+ createInstance('1', types_1.ServiceStatus.DOWN),
137
+ createInstance('2', types_1.ServiceStatus.DOWN),
138
+ ];
139
+ expect(strategy.choose(instances)).toBeNull();
140
+ });
141
+ });
142
+ describe('IPHashStrategy', () => {
143
+ it('should return same instance for same IP', () => {
144
+ const strategy = new strategies_1.IPHashStrategy();
145
+ const instances = [createInstance('1'), createInstance('2'), createInstance('3')];
146
+ const ip = '192.168.1.1';
147
+ const first = strategy.choose(instances, ip);
148
+ const second = strategy.choose(instances, ip);
149
+ expect(first?.id).toBe(second?.id);
150
+ });
151
+ it('should return first instance when no IP provided', () => {
152
+ const strategy = new strategies_1.IPHashStrategy();
153
+ const instances = [createInstance('1'), createInstance('2')];
154
+ const selected = strategy.choose(instances);
155
+ expect(selected?.id).toBe('1');
156
+ });
157
+ it('should return null when no healthy instances', () => {
158
+ const strategy = new strategies_1.IPHashStrategy();
159
+ const instances = [
160
+ createInstance('1', types_1.ServiceStatus.DOWN),
161
+ createInstance('2', types_1.ServiceStatus.DOWN),
162
+ ];
163
+ expect(strategy.choose(instances, '192.168.1.1')).toBeNull();
164
+ });
165
+ });
166
+ describe('ZoneAwareStrategy', () => {
167
+ it('should prefer instances in preferred zone', () => {
168
+ const strategy = new strategies_1.ZoneAwareStrategy('us-east-1');
169
+ const instances = [
170
+ { ...createInstance('1'), zone: 'us-east-1' },
171
+ { ...createInstance('2'), zone: 'us-west-1' },
172
+ { ...createInstance('3'), zone: 'us-east-1' },
173
+ ];
174
+ const selected = strategy.choose(instances);
175
+ expect(selected?.zone).toBe('us-east-1');
176
+ expect(['1', '3']).toContain(selected?.id);
177
+ });
178
+ it('should fallback to any zone when preferred zone not available', () => {
179
+ const strategy = new strategies_1.ZoneAwareStrategy('us-east-1');
180
+ const instances = [
181
+ { ...createInstance('1'), zone: 'us-west-1' },
182
+ { ...createInstance('2'), zone: 'eu-west-1' },
183
+ ];
184
+ const selected = strategy.choose(instances);
185
+ expect(selected).toBeDefined();
186
+ expect(['1', '2']).toContain(selected?.id);
187
+ });
188
+ it('should work without preferred zone', () => {
189
+ const strategy = new strategies_1.ZoneAwareStrategy();
190
+ const instances = [createInstance('1'), createInstance('2')];
191
+ const selected = strategy.choose(instances);
192
+ expect(selected).toBeDefined();
193
+ });
194
+ it('should return null when no healthy instances', () => {
195
+ const strategy = new strategies_1.ZoneAwareStrategy('us-east-1');
196
+ const instances = [
197
+ createInstance('1', types_1.ServiceStatus.DOWN),
198
+ createInstance('2', types_1.ServiceStatus.DOWN),
199
+ ];
200
+ expect(strategy.choose(instances)).toBeNull();
201
+ });
202
+ });
203
+ describe('LoadBalancerFactory', () => {
204
+ it('should register and retrieve strategies', () => {
205
+ const factory = new strategies_1.LoadBalancerFactory();
206
+ const customStrategy = new strategies_1.RoundRobinStrategy();
207
+ customStrategy.name = 'custom';
208
+ factory.register(customStrategy);
209
+ expect(factory.get('custom')).toBe(customStrategy);
210
+ });
211
+ it('should create default strategies', () => {
212
+ const factory = new strategies_1.LoadBalancerFactory();
213
+ expect(factory.get('round-robin')).toBeDefined();
214
+ expect(factory.get('random')).toBeDefined();
215
+ expect(factory.get('least-connections')).toBeDefined();
216
+ expect(factory.get('weighted-round-robin')).toBeDefined();
217
+ expect(factory.get('ip-hash')).toBeDefined();
218
+ });
219
+ it('should create zone-aware strategy with options', () => {
220
+ const factory = new strategies_1.LoadBalancerFactory();
221
+ const strategy = factory.create('zone-aware', { zone: 'us-east-1' });
222
+ expect(strategy).toBeInstanceOf(strategies_1.ZoneAwareStrategy);
223
+ expect(strategy.name).toBe('zone-aware');
224
+ });
225
+ it('should throw error for unknown strategy', () => {
226
+ const factory = new strategies_1.LoadBalancerFactory();
227
+ expect(() => factory.create('unknown-strategy')).toThrow("Load balancing strategy 'unknown-strategy' not found");
228
+ });
229
+ it('should return undefined for non-existent strategy', () => {
230
+ const factory = new strategies_1.LoadBalancerFactory();
231
+ expect(factory.get('non-existent')).toBeUndefined();
232
+ });
233
+ });
234
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Memory Backend Tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=memory-backend.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-backend.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/memory-backend.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /**
3
+ * Memory Backend Tests
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const memory_backend_1 = require("../backends/memory-backend");
7
+ const types_1 = require("../types");
8
+ describe('MemoryRegistryBackend', () => {
9
+ let backend;
10
+ beforeEach(() => {
11
+ backend = new memory_backend_1.MemoryRegistryBackend();
12
+ });
13
+ const createInstance = (id, name = 'test-service') => ({
14
+ id,
15
+ name,
16
+ host: 'localhost',
17
+ port: 3000,
18
+ status: types_1.ServiceStatus.UP,
19
+ lastHeartbeat: new Date(),
20
+ registeredAt: new Date(),
21
+ });
22
+ describe('register', () => {
23
+ it('should register a service instance', async () => {
24
+ const instance = createInstance('1');
25
+ await backend.register(instance);
26
+ const retrieved = await backend.getInstance('1');
27
+ expect(retrieved).toEqual(instance);
28
+ });
29
+ it('should index instances by service name', async () => {
30
+ const instance1 = createInstance('1', 'service-a');
31
+ const instance2 = createInstance('2', 'service-a');
32
+ const instance3 = createInstance('3', 'service-b');
33
+ await backend.register(instance1);
34
+ await backend.register(instance2);
35
+ await backend.register(instance3);
36
+ const instancesA = await backend.getInstances('service-a');
37
+ const instancesB = await backend.getInstances('service-b');
38
+ expect(instancesA).toHaveLength(2);
39
+ expect(instancesB).toHaveLength(1);
40
+ });
41
+ });
42
+ describe('deregister', () => {
43
+ it('should remove a registered instance', async () => {
44
+ const instance = createInstance('1');
45
+ await backend.register(instance);
46
+ await backend.deregister('1');
47
+ const retrieved = await backend.getInstance('1');
48
+ expect(retrieved).toBeNull();
49
+ });
50
+ it('should clean up service index when last instance is removed', async () => {
51
+ const instance = createInstance('1', 'service-a');
52
+ await backend.register(instance);
53
+ const servicesBefore = await backend.getAllServices();
54
+ expect(servicesBefore).toContain('service-a');
55
+ await backend.deregister('1');
56
+ const servicesAfter = await backend.getAllServices();
57
+ expect(servicesAfter).not.toContain('service-a');
58
+ });
59
+ });
60
+ describe('heartbeat', () => {
61
+ it('should update lastHeartbeat and set status to UP', async () => {
62
+ const instance = createInstance('1');
63
+ instance.status = types_1.ServiceStatus.DOWN;
64
+ await backend.register(instance);
65
+ const before = await backend.getInstance('1');
66
+ const beforeTime = before.lastHeartbeat.getTime();
67
+ await new Promise((resolve) => setTimeout(resolve, 10));
68
+ await backend.heartbeat('1');
69
+ const after = await backend.getInstance('1');
70
+ expect(after.lastHeartbeat.getTime()).toBeGreaterThan(beforeTime);
71
+ expect(after.status).toBe(types_1.ServiceStatus.UP);
72
+ });
73
+ });
74
+ describe('getInstances', () => {
75
+ it('should return all instances for a service', async () => {
76
+ const instance1 = createInstance('1', 'service-a');
77
+ const instance2 = createInstance('2', 'service-a');
78
+ const instance3 = createInstance('3', 'service-b');
79
+ await backend.register(instance1);
80
+ await backend.register(instance2);
81
+ await backend.register(instance3);
82
+ const instances = await backend.getInstances('service-a');
83
+ expect(instances).toHaveLength(2);
84
+ expect(instances.map((i) => i.id)).toEqual(['1', '2']);
85
+ });
86
+ it('should return empty array for non-existent service', async () => {
87
+ const instances = await backend.getInstances('non-existent');
88
+ expect(instances).toEqual([]);
89
+ });
90
+ it('should filter by zone', async () => {
91
+ const instance1 = { ...createInstance('1'), zone: 'us-east-1' };
92
+ const instance2 = { ...createInstance('2'), zone: 'us-west-1' };
93
+ const instance3 = { ...createInstance('3'), zone: 'us-east-1' };
94
+ await backend.register(instance1);
95
+ await backend.register(instance2);
96
+ await backend.register(instance3);
97
+ const instances = await backend.getInstances('test-service', {
98
+ zone: 'us-east-1',
99
+ });
100
+ expect(instances).toHaveLength(2);
101
+ expect(instances.every((i) => i.zone === 'us-east-1')).toBe(true);
102
+ });
103
+ it('should filter by status', async () => {
104
+ const instance1 = { ...createInstance('1'), status: types_1.ServiceStatus.UP };
105
+ const instance2 = { ...createInstance('2'), status: types_1.ServiceStatus.DOWN };
106
+ const instance3 = { ...createInstance('3'), status: types_1.ServiceStatus.UP };
107
+ await backend.register(instance1);
108
+ await backend.register(instance2);
109
+ await backend.register(instance3);
110
+ const instances = await backend.getInstances('test-service', {
111
+ status: types_1.ServiceStatus.UP,
112
+ });
113
+ expect(instances).toHaveLength(2);
114
+ expect(instances.every((i) => i.status === types_1.ServiceStatus.UP)).toBe(true);
115
+ });
116
+ it('should filter by tags', async () => {
117
+ const instance1 = { ...createInstance('1'), tags: ['web', 'api'] };
118
+ const instance2 = { ...createInstance('2'), tags: ['api'] };
119
+ const instance3 = { ...createInstance('3'), tags: ['web'] };
120
+ await backend.register(instance1);
121
+ await backend.register(instance2);
122
+ await backend.register(instance3);
123
+ const instances = await backend.getInstances('test-service', {
124
+ tags: ['web', 'api'],
125
+ });
126
+ expect(instances).toHaveLength(1);
127
+ expect(instances[0].id).toBe('1');
128
+ });
129
+ it('should filter by metadata', async () => {
130
+ const instance1 = {
131
+ ...createInstance('1'),
132
+ metadata: { version: '1.0.0', env: 'prod' },
133
+ };
134
+ const instance2 = {
135
+ ...createInstance('2'),
136
+ metadata: { version: '2.0.0', env: 'prod' },
137
+ };
138
+ const instance3 = {
139
+ ...createInstance('3'),
140
+ metadata: { version: '1.0.0', env: 'dev' },
141
+ };
142
+ await backend.register(instance1);
143
+ await backend.register(instance2);
144
+ await backend.register(instance3);
145
+ const instances = await backend.getInstances('test-service', {
146
+ metadata: { version: '1.0.0' },
147
+ });
148
+ expect(instances).toHaveLength(2);
149
+ expect(instances.every((i) => i.metadata?.version === '1.0.0')).toBe(true);
150
+ });
151
+ it('should combine multiple filters', async () => {
152
+ const instance1 = {
153
+ ...createInstance('1'),
154
+ zone: 'us-east-1',
155
+ status: types_1.ServiceStatus.UP,
156
+ tags: ['web'],
157
+ };
158
+ const instance2 = {
159
+ ...createInstance('2'),
160
+ zone: 'us-east-1',
161
+ status: types_1.ServiceStatus.DOWN,
162
+ tags: ['web'],
163
+ };
164
+ const instance3 = {
165
+ ...createInstance('3'),
166
+ zone: 'us-west-1',
167
+ status: types_1.ServiceStatus.UP,
168
+ tags: ['web'],
169
+ };
170
+ await backend.register(instance1);
171
+ await backend.register(instance2);
172
+ await backend.register(instance3);
173
+ const instances = await backend.getInstances('test-service', {
174
+ zone: 'us-east-1',
175
+ status: types_1.ServiceStatus.UP,
176
+ tags: ['web'],
177
+ });
178
+ expect(instances).toHaveLength(1);
179
+ expect(instances[0].id).toBe('1');
180
+ });
181
+ });
182
+ describe('getInstance', () => {
183
+ it('should return instance by ID', async () => {
184
+ const instance = createInstance('1');
185
+ await backend.register(instance);
186
+ const retrieved = await backend.getInstance('1');
187
+ expect(retrieved).toEqual(instance);
188
+ });
189
+ it('should return null for non-existent instance', async () => {
190
+ const retrieved = await backend.getInstance('non-existent');
191
+ expect(retrieved).toBeNull();
192
+ });
193
+ });
194
+ describe('getAllServices', () => {
195
+ it('should return all registered service names', async () => {
196
+ await backend.register(createInstance('1', 'service-a'));
197
+ await backend.register(createInstance('2', 'service-a'));
198
+ await backend.register(createInstance('3', 'service-b'));
199
+ const services = await backend.getAllServices();
200
+ expect(services.sort()).toEqual(['service-a', 'service-b']);
201
+ });
202
+ it('should return empty array when no services registered', async () => {
203
+ const services = await backend.getAllServices();
204
+ expect(services).toEqual([]);
205
+ });
206
+ });
207
+ describe('updateStatus', () => {
208
+ it('should update instance status', async () => {
209
+ const instance = createInstance('1');
210
+ await backend.register(instance);
211
+ await backend.updateStatus('1', types_1.ServiceStatus.DOWN);
212
+ const updated = await backend.getInstance('1');
213
+ expect(updated.status).toBe(types_1.ServiceStatus.DOWN);
214
+ await backend.updateStatus('1', types_1.ServiceStatus.UP);
215
+ const updated2 = await backend.getInstance('1');
216
+ expect(updated2.status).toBe(types_1.ServiceStatus.UP);
217
+ });
218
+ it('should not throw error for non-existent instance', async () => {
219
+ await expect(backend.updateStatus('non-existent', types_1.ServiceStatus.DOWN)).resolves.not.toThrow();
220
+ });
221
+ });
222
+ describe('cleanup', () => {
223
+ it('should remove expired instances', async () => {
224
+ const backend = new memory_backend_1.MemoryRegistryBackend(100); // 100ms expiration
225
+ const instance = createInstance('1');
226
+ await backend.register(instance);
227
+ // Wait for expiration
228
+ await new Promise((resolve) => setTimeout(resolve, 150));
229
+ await backend.cleanup();
230
+ const retrieved = await backend.getInstance('1');
231
+ expect(retrieved).toBeNull();
232
+ });
233
+ it('should not remove active instances', async () => {
234
+ const backend = new memory_backend_1.MemoryRegistryBackend(100);
235
+ const instance = createInstance('1');
236
+ await backend.register(instance);
237
+ // Update heartbeat before expiration
238
+ await new Promise((resolve) => setTimeout(resolve, 50));
239
+ await backend.heartbeat('1');
240
+ await new Promise((resolve) => setTimeout(resolve, 50));
241
+ await backend.cleanup();
242
+ const retrieved = await backend.getInstance('1');
243
+ expect(retrieved).toBeDefined();
244
+ });
245
+ });
246
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Service Client Tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=service-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/service-client.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}