Skip to content

Commit f82e8e6

Browse files
kriszypclaude
andcommitted
feat(unitTests/apiTests): use loopback address pool for port isolation
Each mocha run now acquires a unique 127.0.0.x address from the @harperfast/integration-testing loopback pool before starting the in-process Harper server, so concurrent agent runs bind to different addresses and never collide on ports 9925/9926/1883/8883. - setupTestApp.mjs: call getNextAvailableLoopbackAddress() on first server start; update all setProperty() port bindings (HTTP, MQTT, MQTT-TLS, operations API) to use the acquired address; export baseUrl/wsBaseUrl/operationsUrl/mqttUrl/mqttsUrl/testHost live bindings so test files see the address without any re-import; release address on beforeExit; set HARPER_TEST_HOST env var for CJS callers - utility.js: read HARPER_TEST_HOST / HARPER_TEST_OPS_PORT env vars so callOperation() reaches the correct operations API address - All *-test.mjs files: import the relevant URL variables and replace every hardcoded http://localhost:9926, ws://localhost:9926, http://localhost:9925, mqtt://localhost:1883, mqtts://localhost:8883 reference; also bind the ephemeral mTLS test listeners (8884/8885) to testHost so they follow the loopback assignment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent caeb666 commit f82e8e6

12 files changed

Lines changed: 246 additions & 209 deletions

unitTests/apiTests/RBAC-test.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { callOperation } from './utility.js';
22
import axios from 'axios';
3-
import { setupTestApp } from './setupTestApp.mjs';
3+
import { setupTestApp, baseUrl } from './setupTestApp.mjs';
44
import { expect } from 'chai';
55

66
describe('test declared role', () => {
@@ -32,13 +32,13 @@ describe('test declared role', () => {
3232
headers
3333
);
3434
expect(response.status).to.eq(200);
35-
response = await axios('http://localhost:9926/SimpleRecord/6', {
35+
response = await axios(`${baseUrl}/SimpleRecord/6`, {
3636
headers,
3737
});
3838
expect(response.status).to.eq(200);
3939
let perm_error;
4040
try {
41-
response = await axios.put('http://localhost:9926/SimpleRecord/6', { name: 'test change' }, { headers });
41+
response = await axios.put(`${baseUrl}/SimpleRecord/6`, { name: 'test change' }, { headers });
4242
} catch (error) {
4343
perm_error = error;
4444
}

unitTests/apiTests/RESTProperties-test.mjs

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { assert } from 'chai';
44
import axios from 'axios';
5-
import { setupTestApp } from './setupTestApp.mjs';
5+
import { setupTestApp, baseUrl, operationsUrl, testHost } from './setupTestApp.mjs';
66
import { request } from 'http';
77

88
describe('test REST with property updates', function () {
@@ -11,53 +11,53 @@ describe('test REST with property updates', function () {
1111
});
1212

1313
it('post with sub-property manipulation', async () => {
14-
let response = await axios.put('http://localhost:9926/namespace/SubObject/5', {
14+
let response = await axios.put(`${baseUrl}/namespace/SubObject/5`, {
1515
id: '5',
1616
subObject: { name: 'a sub-object' },
1717
subArray: [{ name: 'a sub-object of an array' }],
1818
});
1919
assert.equal(response.status, 204);
20-
response = await axios.post('http://localhost:9926/namespace/SubObject/5', {
20+
response = await axios.post(`${baseUrl}/namespace/SubObject/5`, {
2121
subPropertyValue: 'a new value',
2222
subArrayItem: { name: 'a new item' },
2323
});
2424
assert.equal(response.status, 200);
25-
response = await axios.get('http://localhost:9926/namespace/SubObject/5');
25+
response = await axios.get(`${baseUrl}/namespace/SubObject/5`);
2626
assert.equal(response.status, 200);
2727
assert.equal(response.data.subObject.subProperty, 'a new value');
2828
assert.equal(response.data.subArray[1].name, 'a new item');
2929
});
3030
it('get with sub-property access via dot', async () => {
31-
let response = await axios.put('http://localhost:9926/namespace/SubObject/6', {
31+
let response = await axios.put(`${baseUrl}/namespace/SubObject/6`, {
3232
id: '5',
3333
subObject: { name: 'a sub-object' },
3434
subArray: [{ name: 'a sub-object of an array' }],
3535
extraProperty: 'this is not in the schema',
3636
});
3737
assert.equal(response.status, 204);
38-
response = await axios.get('http://localhost:9926/namespace/SubObject/6.subObject');
38+
response = await axios.get(`${baseUrl}/namespace/SubObject/6.subObject`);
3939
assert.equal(response.status, 200);
4040
assert.equal(response.data.name, 'a sub-object');
4141
// this should return 404 because the property is not in the schema (and should be treated as a full id)
42-
response = await axios.get('http://localhost:9926/namespace/SubObject/6.extraProperty', {
42+
response = await axios.get(`${baseUrl}/namespace/SubObject/6.extraProperty`, {
4343
validateStatus: function () {
4444
return true;
4545
},
4646
});
4747
assert.equal(response.status, 404);
4848
});
4949
it('get with sub-property access via ?select', async () => {
50-
let response = await axios.put('http://localhost:9926/namespace/SubObject/6', {
50+
let response = await axios.put(`${baseUrl}/namespace/SubObject/6`, {
5151
id: '5',
5252
any: { name: 'can be an object' },
5353
subObject: { name: 'a sub-object' },
5454
subArray: [{ name: 'a sub-object of an array' }],
5555
});
5656
assert.equal(response.status, 204);
57-
response = await axios.get('http://localhost:9926/namespace/SubObject/6?select(subObject)');
57+
response = await axios.get(`${baseUrl}/namespace/SubObject/6?select(subObject)`);
5858
assert.equal(response.status, 200);
5959
assert.equal(response.data.name, 'a sub-object');
60-
response = await axios.get('http://localhost:9926/namespace/SubObject/6?select(any,)');
60+
response = await axios.get(`${baseUrl}/namespace/SubObject/6?select(any,)`);
6161
assert.equal(response.data.any.name, 'can be an object');
6262
});
6363
it('put with wrong type on attribute', async () => {
@@ -67,7 +67,7 @@ describe('test REST with property updates', function () {
6767
'accept': 'application/json',
6868
};
6969
let response = await axios.put(
70-
'http://localhost:9926/FourProp/555',
70+
`${baseUrl}/FourProp/555`,
7171
JSON.stringify({
7272
id: '555',
7373
name: 33,
@@ -86,52 +86,52 @@ describe('test REST with property updates', function () {
8686
});
8787

8888
it('put with nested path', async () => {
89-
let response = await axios.put('http://localhost:9926/namespace/SubObject/multi/part/id/3', {
89+
let response = await axios.put(`${baseUrl}/namespace/SubObject/multi/part/id/3`, {
9090
any: 'can be a string',
9191
subObject: { name: 'deeply nested' },
9292
subArray: [],
9393
});
9494
assert.equal(response.status, 204);
95-
response = await axios.get('http://localhost:9926/namespace/SubObject/multi/part/id/3');
95+
response = await axios.get(`${baseUrl}/namespace/SubObject/multi/part/id/3`);
9696
assert.equal(response.status, 200);
9797
assert.equal(response.data.subObject.name, 'deeply nested');
9898
assert.deepEqual(response.data.id, 'multi/part/id/3');
9999
assert.deepEqual(response.data.any, 'can be a string');
100-
response = await axios.get('http://localhost:9926/namespace/SubObject/multi/');
100+
response = await axios.get(`${baseUrl}/namespace/SubObject/multi/`);
101101
assert.equal(response.status, 200);
102102
assert.equal(response.data[0].subObject.name, 'deeply nested');
103103
assert.equal(response.data.length, 1);
104-
response = await axios.get('http://localhost:9926/namespace/SubObject/multi/part/');
104+
response = await axios.get(`${baseUrl}/namespace/SubObject/multi/part/`);
105105
assert.equal(response.status, 200);
106106
assert.equal(response.data[0].subObject.name, 'deeply nested');
107107
assert.equal(response.data.length, 1);
108-
response = await axios.get('http://localhost:9926/namespace/SubObject/multi/?any=not-here');
108+
response = await axios.get(`${baseUrl}/namespace/SubObject/multi/?any=not-here`);
109109
assert.equal(response.status, 200);
110110
assert.equal(response.data.length, 0);
111111

112-
response = await axios.delete('http://localhost:9926/namespace/SubObject/multi/part/');
113-
response = await axios.get('http://localhost:9926/namespace/SubObject/multi/part/');
112+
response = await axios.delete(`${baseUrl}/namespace/SubObject/multi/part/`);
113+
response = await axios.get(`${baseUrl}/namespace/SubObject/multi/part/`);
114114
assert.equal(response.status, 200);
115115
assert.equal(response.data.length, 0);
116116
});
117117

118118
it('put with encoded slashes, dots', async () => {
119-
let response = await axios.put('http://localhost:9926/namespace/SubObject/i%2Flike%2Fslashes%2E', {
119+
let response = await axios.put(`${baseUrl}/namespace/SubObject/i%2Flike%2Fslashes%2E`, {
120120
any: 'can be a string',
121121
subObject: { name: 'deeply nested' },
122122
subArray: [],
123123
});
124124
assert.equal(response.status, 204);
125-
response = await axios.get('http://localhost:9926/namespace/SubObject/i%2Flike%2Fslashes%2E');
125+
response = await axios.get(`${baseUrl}/namespace/SubObject/i%2Flike%2Fslashes%2E`);
126126
assert.equal(response.status, 200);
127127
});
128128

129129
it('get with timestamps and no PK on record', async () => {
130-
let response = await axios.put('http://localhost:9926/HasTimeStampsNoPK/33', {
130+
let response = await axios.put(`${baseUrl}/HasTimeStampsNoPK/33`, {
131131
name: 'Look Ma, no primary key!',
132132
});
133133
assert.equal(response.status, 204);
134-
response = await axios.get('http://localhost:9926/HasTimeStampsNoPK/33');
134+
response = await axios.get(`${baseUrl}/HasTimeStampsNoPK/33`);
135135
assert.equal(response.status, 200);
136136
assert.equal(response.data.name, 'Look Ma, no primary key!');
137137
assert(response.data.updated > 1689025407526);
@@ -140,7 +140,7 @@ describe('test REST with property updates', function () {
140140
});
141141

142142
it('check headers on get', async () => {
143-
await axios.get('http://localhost:9926/namespace/SubObject/6', {
143+
await axios.get(`${baseUrl}/namespace/SubObject/6`, {
144144
headers: {
145145
'Custom-Header': 'custom-value',
146146
},
@@ -160,42 +160,42 @@ describe('test REST with property updates', function () {
160160

161161
describe('joins', async () => {
162162
before(async () => {
163-
await axios.put('http://localhost:9926/Related/6', {
163+
await axios.put(`${baseUrl}/Related/6`, {
164164
id: '6',
165165
name: 'Related 6',
166166
another: 'Another value',
167167
});
168-
await axios.put('http://localhost:9926/namespace/SubObject/33', {
168+
await axios.put(`${baseUrl}/namespace/SubObject/33`, {
169169
id: '33',
170170
subObject: { name: 'a sub-object' },
171171
subArray: [{ name: 'a sub-object of an array' }],
172172
relatedId: '6',
173173
});
174-
await axios.put('http://localhost:9926/namespace/SubObject/34', {
174+
await axios.put(`${baseUrl}/namespace/SubObject/34`, {
175175
id: '34',
176176
subObject: { name: 'a sub-object' },
177177
subArray: [{ name: 'a sub-object of an array' }],
178178
relatedId: '6',
179179
});
180180
});
181181
it('get data with related data joined', async function () {
182-
let response = await axios.get('http://localhost:9926/namespace/SubObject/34?select(id,related)');
182+
let response = await axios.get(`${baseUrl}/namespace/SubObject/34?select(id,related)`);
183183
assert.equal(response.status, 200);
184184
assert.equal(response.data.related.name, 'Related 6');
185185
});
186186
it('query for data with related data joined', async function () {
187-
let response = await axios.get('http://localhost:9926/namespace/SubObject/?id=33&select(id,related)');
187+
let response = await axios.get(`${baseUrl}/namespace/SubObject/?id=33&select(id,related)`);
188188
assert.equal(response.status, 200);
189189
assert.equal(response.data[0].related.name, 'Related 6');
190190
});
191191
it('query for data by related id with related data joined', async function () {
192-
let response = await axios.get('http://localhost:9926/namespace/SubObject/?relatedId=6&select(id,related)');
192+
let response = await axios.get(`${baseUrl}/namespace/SubObject/?relatedId=6&select(id,related)`);
193193
assert.equal(response.status, 200);
194194
assert.equal(response.data[1].related.name, 'Related 6');
195195
});
196196
it('query for data by related value with related data joined', async function () {
197197
let response = await axios.get(
198-
'http://localhost:9926/namespace/SubObject/?related.name=Related 6' + '&sort(-related.name)&select(id,related)'
198+
`${baseUrl}/namespace/SubObject/?related.name=Related 6` + '&sort(-related.name)&select(id,related)'
199199
);
200200
assert.equal(response.status, 200);
201201
assert.equal(response.data[1].related.name, 'Related 6');
@@ -205,7 +205,7 @@ describe('test REST with property updates', function () {
205205
let response = await new Promise((resolve) => {
206206
let req = request(
207207
{
208-
hostname: 'localhost',
208+
hostname: testHost,
209209
method: 'GET',
210210
port: 9926,
211211
path:
@@ -233,7 +233,7 @@ describe('test REST with property updates', function () {
233233
});
234234
describe('check operations', function () {
235235
it('search_by_value returns all attributes', async function () {
236-
let response = await axios.post('http://localhost:9925', {
236+
let response = await axios.post(operationsUrl, {
237237
operation: 'search_by_value',
238238
schema: 'data',
239239
table: 'FourProp',
@@ -244,7 +244,7 @@ describe('test REST with property updates', function () {
244244
});
245245
it('search_by_conditions with join', async function () {
246246
let response = await axios.post(
247-
'http://localhost:9925',
247+
operationsUrl,
248248
{
249249
operation: 'search_by_conditions',
250250
schema: 'data',
@@ -262,7 +262,7 @@ describe('test REST with property updates', function () {
262262
});
263263
it('search_by_conditions with different properties', async function () {
264264
let response = await axios.post(
265-
'http://localhost:9925',
265+
operationsUrl,
266266
{
267267
operation: 'search_by_conditions',
268268
table: 'SubObject',
@@ -299,7 +299,7 @@ describe('test REST with property updates', function () {
299299

300300
it('sql returns all attributes of four property object', async function () {
301301
let response = await axios.post(
302-
'http://localhost:9925',
302+
operationsUrl,
303303
{
304304
operation: 'sql',
305305
sql: 'SELECT * FROM data.FourProp',
@@ -314,19 +314,19 @@ describe('test REST with property updates', function () {
314314
assert(response.data.some((record) => record.title === 'title0'));
315315
});
316316
it('sql returns all attributes and sub-object of array', async function () {
317-
let response = await axios.put('http://localhost:9926/namespace/SubObject/6', {
317+
let response = await axios.put(`${baseUrl}/namespace/SubObject/6`, {
318318
id: '6',
319319
subObject: { name: 'another sub-object' },
320320
subArray: [{ name: 'another sub-object of an array' }],
321321
});
322-
response = await axios.post('http://localhost:9925', {
322+
response = await axios.post(operationsUrl, {
323323
operation: 'sql',
324324
sql: 'SELECT * FROM data.SubObject',
325325
});
326326
assert(response.data.some((record) => record.subObject?.name === 'another sub-object'));
327327
});
328328
it('describe_table returns all attributes', async function () {
329-
let response = await axios.post('http://localhost:9925', {
329+
let response = await axios.post(operationsUrl, {
330330
operation: 'describe_table',
331331
schema: 'data',
332332
table: 'FourProp',
@@ -337,7 +337,7 @@ describe('test REST with property updates', function () {
337337
assert(!response.data.attributes.find((attr) => attr.attribute === 'ageInMonths'));
338338
});
339339
it('describe_table with include_computed returns all attributes', async function () {
340-
let response = await axios.post('http://localhost:9925', {
340+
let response = await axios.post(operationsUrl, {
341341
operation: 'describe_table',
342342
schema: 'data',
343343
table: 'FourProp',
@@ -361,17 +361,17 @@ describe('test REST with property updates with loadAsInstance=false', function (
361361
});
362362

363363
it('get with sub-property access via ?select', async () => {
364-
let response = await axios.put('http://localhost:9926/namespace/SubObject/6', {
364+
let response = await axios.put(`${baseUrl}/namespace/SubObject/6`, {
365365
id: '5',
366366
any: { name: 'can be an object' },
367367
subObject: { name: 'a sub-object' },
368368
subArray: [{ name: 'a sub-object of an array' }],
369369
});
370370
assert.equal(response.status, 204);
371-
response = await axios.get('http://localhost:9926/namespace/SubObject/6?select(subObject)');
371+
response = await axios.get(`${baseUrl}/namespace/SubObject/6?select(subObject)`);
372372
assert.equal(response.status, 200);
373373
assert.equal(response.data.name, 'a sub-object');
374-
response = await axios.get('http://localhost:9926/namespace/SubObject/6?select(any,)');
374+
response = await axios.get(`${baseUrl}/namespace/SubObject/6?select(any,)`);
375375
assert.equal(response.data.any.name, 'can be an object');
376376
});
377377

0 commit comments

Comments
 (0)