@owlmeans/test-integration 0.1.18-rc.30 → 0.1.18-rc.32
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/README.md +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/testing-integration/SKILL.md +1 -1
- package/build/cleanup.d.ts +2 -3
- package/build/cleanup.d.ts.map +1 -1
- package/build/cleanup.js +4 -2
- package/build/cleanup.js.map +1 -1
- package/build/consts.local.d.ts +6 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +28 -0
- package/build/consts.local.js.map +1 -0
- package/build/gates.d.ts +1 -38
- package/build/gates.d.ts.map +1 -1
- package/build/gates.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/reachability.d.ts +1 -13
- package/build/reachability.d.ts.map +1 -1
- package/build/reachability.js +13 -28
- package/build/reachability.js.map +1 -1
- package/build/types.d.ts +43 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/types.local.d.ts +13 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/package.json +4 -3
- package/src/cleanup.ts +6 -3
- package/src/consts.local.ts +29 -0
- package/src/gates.ts +2 -57
- package/src/index.ts +1 -0
- package/src/reachability.ts +6 -36
- package/src/types.local.ts +13 -0
- package/src/types.ts +49 -0
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
19
19
|
your project's skill store (`.agents/skills/`):
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
22
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.48
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/test-integration",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.32",
|
|
5
|
+
"generatedAt": "2026-10-05T08:32:19.106Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -6,7 +6,7 @@ description: Category-C integration tests for OwlMeans Common packages that talk
|
|
|
6
6
|
|
|
7
7
|
# Integration Tests — Category C
|
|
8
8
|
|
|
9
|
-
**Install:** `"@owlmeans/test-integration": "^0.1.18-rc.
|
|
9
|
+
**Install:** `"@owlmeans/test-integration": "^0.1.18-rc.32"` in `devDependencies`
|
|
10
10
|
|
|
11
11
|
Category C applies to packages that integrate with external services: `postgres`,
|
|
12
12
|
`postgres-resource`, `mongo`, `mongo-resource`, `redis`, `redis-resource`, `redis-queue`,
|
package/build/cleanup.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type CleanupFn
|
|
1
|
+
import type { CleanupFn } from './types.local.js';
|
|
2
2
|
/**
|
|
3
3
|
* Register a cleanup function that `runCleanups()` will execute (LIFO).
|
|
4
4
|
* Use from `tests/context.ts` `setup()` to schedule teardown of resources
|
|
@@ -8,9 +8,8 @@ type CleanupFn = () => void | Promise<void>;
|
|
|
8
8
|
export declare const registerCleanup: (fn: CleanupFn) => void;
|
|
9
9
|
/**
|
|
10
10
|
* Run all pending cleanup functions in reverse registration order.
|
|
11
|
-
* Errors are swallowed and
|
|
11
|
+
* Errors are swallowed and logged as warnings so a failing cleanup
|
|
12
12
|
* cannot mask a test failure.
|
|
13
13
|
*/
|
|
14
14
|
export declare const runCleanups: () => Promise<void>;
|
|
15
|
-
export {};
|
|
16
15
|
//# sourceMappingURL=cleanup.d.ts.map
|
package/build/cleanup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAMjD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,OAAQ,SAAS,KAAG,IAE/C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAUhD,CAAA"}
|
package/build/cleanup.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logger } from '@owlmeans/log';
|
|
2
|
+
const log = logger('test-integration');
|
|
1
3
|
const queue = [];
|
|
2
4
|
/**
|
|
3
5
|
* Register a cleanup function that `runCleanups()` will execute (LIFO).
|
|
@@ -10,7 +12,7 @@ export const registerCleanup = (fn) => {
|
|
|
10
12
|
};
|
|
11
13
|
/**
|
|
12
14
|
* Run all pending cleanup functions in reverse registration order.
|
|
13
|
-
* Errors are swallowed and
|
|
15
|
+
* Errors are swallowed and logged as warnings so a failing cleanup
|
|
14
16
|
* cannot mask a test failure.
|
|
15
17
|
*/
|
|
16
18
|
export const runCleanups = async () => {
|
|
@@ -22,7 +24,7 @@ export const runCleanups = async () => {
|
|
|
22
24
|
await fn();
|
|
23
25
|
}
|
|
24
26
|
catch (err) {
|
|
25
|
-
|
|
27
|
+
log.warn('Cleanup failed', err);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
};
|
package/build/cleanup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAGtC,MAAM,GAAG,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAA;AAEtC,MAAM,KAAK,GAAgB,EAAE,CAAA;AAE7B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAa,EAAQ,EAAE;IACrD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAChB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;IACnD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,EAAE,IAAI,IAAI;YAAE,SAAQ;QACxB,IAAI,CAAC;YACH,MAAM,EAAE,EAAE,CAAA;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** How long one connect may take before the address counts as unreachable. */
|
|
2
|
+
export declare const CONNECT_TIMEOUT_MS = 1500;
|
|
3
|
+
/** The probe runs in a child process; this covers its own startup on a loaded machine. */
|
|
4
|
+
export declare const SPAWN_GRACE_MS = 5000;
|
|
5
|
+
export declare const PROBE_SCRIPT = "\nconst net = require('net')\nconst [targets, timeout] = JSON.parse(process.env.OWLMEANS_TEST_PROBE)\nlet pending = targets.length\nlet last = ''\nfor (const target of targets) {\n let settled = false\n const socket = net.connect({ host: target.host, port: target.port })\n const fail = code => {\n if (settled) return\n settled = true\n socket.destroy()\n last = code\n if (--pending === 0) { process.stdout.write(last); process.exit(1) }\n }\n socket.setTimeout(timeout)\n socket.once('connect', () => process.exit(0))\n socket.once('timeout', () => fail('ETIMEDOUT'))\n socket.once('error', error => fail(error.code || String(error)))\n}\n";
|
|
6
|
+
//# sourceMappingURL=consts.local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,OAAQ,CAAA;AAEvC,0FAA0F;AAC1F,eAAO,MAAM,cAAc,OAAQ,CAAA;AAInC,eAAO,MAAM,YAAY,8pBAoBxB,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** How long one connect may take before the address counts as unreachable. */
|
|
2
|
+
export const CONNECT_TIMEOUT_MS = 1_500;
|
|
3
|
+
/** The probe runs in a child process; this covers its own startup on a loaded machine. */
|
|
4
|
+
export const SPAWN_GRACE_MS = 5_000;
|
|
5
|
+
// Runs under whichever runtime runs the suite (`process.execPath`), so it is plain CommonJS
|
|
6
|
+
// that both `bun -e` and `node -e` evaluate.
|
|
7
|
+
export const PROBE_SCRIPT = `
|
|
8
|
+
const net = require('net')
|
|
9
|
+
const [targets, timeout] = JSON.parse(process.env.OWLMEANS_TEST_PROBE)
|
|
10
|
+
let pending = targets.length
|
|
11
|
+
let last = ''
|
|
12
|
+
for (const target of targets) {
|
|
13
|
+
let settled = false
|
|
14
|
+
const socket = net.connect({ host: target.host, port: target.port })
|
|
15
|
+
const fail = code => {
|
|
16
|
+
if (settled) return
|
|
17
|
+
settled = true
|
|
18
|
+
socket.destroy()
|
|
19
|
+
last = code
|
|
20
|
+
if (--pending === 0) { process.stdout.write(last); process.exit(1) }
|
|
21
|
+
}
|
|
22
|
+
socket.setTimeout(timeout)
|
|
23
|
+
socket.once('connect', () => process.exit(0))
|
|
24
|
+
socket.once('timeout', () => fail('ETIMEDOUT'))
|
|
25
|
+
socket.once('error', error => fail(error.code || String(error)))
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
//# sourceMappingURL=consts.local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEvC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAA;AAEnC,4FAA4F;AAC5F,6CAA6C;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;CAoB3B,CAAA"}
|
package/build/gates.d.ts
CHANGED
|
@@ -1,45 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
skip: boolean;
|
|
3
|
-
reason?: string;
|
|
4
|
-
env: Partial<E>;
|
|
5
|
-
}
|
|
6
|
-
export interface MongoEnv {
|
|
7
|
-
MONGO_URL: string;
|
|
8
|
-
MONGO_TEST_DB_PREFIX: string;
|
|
9
|
-
}
|
|
1
|
+
import type { IntegrationGate, KubeEnv, MongoEnv, PostgresEnv, RedisEnv, S3Env, SmtpEnv } from './types.js';
|
|
10
2
|
export declare const mongoGate: () => IntegrationGate<MongoEnv>;
|
|
11
|
-
export interface RedisEnv {
|
|
12
|
-
REDIS_URL: string;
|
|
13
|
-
REDIS_TEST_KEY_PREFIX: string;
|
|
14
|
-
}
|
|
15
3
|
export declare const redisGate: () => IntegrationGate<RedisEnv>;
|
|
16
|
-
export interface S3Env {
|
|
17
|
-
S3_ENDPOINT: string;
|
|
18
|
-
S3_KEY: string;
|
|
19
|
-
S3_SECRET: string;
|
|
20
|
-
S3_TEST_BUCKET: string;
|
|
21
|
-
S3_REGION: string;
|
|
22
|
-
}
|
|
23
4
|
export declare const s3Gate: () => IntegrationGate<S3Env>;
|
|
24
|
-
export interface KubeEnv {
|
|
25
|
-
KUBE_CONFIG: string;
|
|
26
|
-
KUBE_TEST_OK: string;
|
|
27
|
-
}
|
|
28
5
|
export declare const kubeGate: () => IntegrationGate<KubeEnv>;
|
|
29
|
-
export interface PostgresEnv {
|
|
30
|
-
POSTGRES_URL: string;
|
|
31
|
-
POSTGRES_TEST_DB_PREFIX: string;
|
|
32
|
-
}
|
|
33
6
|
export declare const postgresGate: () => IntegrationGate<PostgresEnv>;
|
|
34
|
-
export interface SmtpEnv {
|
|
35
|
-
SMTP_HOST: string;
|
|
36
|
-
SMTP_PORT: string;
|
|
37
|
-
SMTP_SECURE: string;
|
|
38
|
-
SMTP_USER: string;
|
|
39
|
-
SMTP_PASSWORD: string;
|
|
40
|
-
SMTP_FROM: string;
|
|
41
|
-
/** Mailbox the specs deliver to. Required — these tests send real mail. */
|
|
42
|
-
SMTP_TEST_TO: string;
|
|
43
|
-
}
|
|
44
7
|
export declare const smtpGate: () => IntegrationGate<SmtpEnv>;
|
|
45
8
|
//# sourceMappingURL=gates.d.ts.map
|
package/build/gates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAGA,
|
|
1
|
+
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAyB3G,eAAO,MAAM,SAAS,QAAO,eAAe,CAAC,QAAQ,CAC2D,CAAA;AAEhH,eAAO,MAAM,SAAS,QAAO,eAAe,CAAC,QAAQ,CAC2D,CAAA;AAEhH,eAAO,MAAM,MAAM,QAAO,eAAe,CAAC,KAAK,CAI5C,CAAA;AAEH,eAAO,MAAM,QAAQ,QAAO,eAAe,CAAC,OAAO,CACU,CAAA;AAE7D,eAAO,MAAM,YAAY,QAAO,eAAe,CAAC,WAAW,CACgE,CAAA;AAE3H,eAAO,MAAM,QAAQ,QAAO,eAAe,CAAC,OAAO,CAIhD,CAAA"}
|
package/build/gates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAIrD,MAAM,OAAO,GAAG,CAAI,IAA0B,EAAc,EAAE;IAC5D,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,MAAM,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAW,CAAA;IAClD,CAAC;IACD,OAAO,GAAiB,CAAA;AAC1B,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,CACxB,QAA8B,EAC9B,QAAQ,GAAyB,EAAE,EACnC,SAAwB,EACJ,EAAE;IACtB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,MAAM,GAAG,GAAG,OAAO,CAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAA;IAClD,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAA;IACpE,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAW,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAC5G,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACxD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;AAC7B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,GAA8B,EAAE,CACvD,iBAAiB,CAAW,CAAC,WAAW,CAAC,EAAE,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAA;AAEhH,MAAM,CAAC,MAAM,SAAS,GAAG,GAA8B,EAAE,CACvD,iBAAiB,CAAW,CAAC,WAAW,CAAC,EAAE,CAAC,uBAAuB,CAAC,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;AAEhH,MAAM,CAAC,MAAM,MAAM,GAAG,GAA2B,EAAE,CACjD,iBAAiB,CACf,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,CAAC,EACxD,CAAC,WAAW,CAAC,CACd,CAAA;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,GAA6B,EAAE,CACrD,iBAAiB,CAAU,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAA;AAE7D,MAAM,CAAC,MAAM,YAAY,GAAG,GAAiC,EAAE,CAC7D,iBAAiB,CAAc,CAAC,cAAc,CAAC,EAAE,CAAC,yBAAyB,CAAC,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;AAE3H,MAAM,CAAC,MAAM,QAAQ,GAAG,GAA6B,EAAE,CACrD,iBAAiB,CACf,CAAC,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,CAAC,EACxE,CAAC,WAAW,EAAE,aAAa,CAAC,CAC7B,CAAA"}
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAE1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
|
package/build/reachability.d.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Whether the server a connection string names answers at all — asked synchronously, because
|
|
3
|
-
* Bun decides `test` vs `test.skip` synchronously and every gate is read at module scope.
|
|
4
|
-
*
|
|
5
|
-
* A populated variable says where the service should be, not that it is there: a developer's
|
|
6
|
-
* `.env` keeps the port-forward address long after the forward is gone, and a suite that trusts
|
|
7
|
-
* it spends its whole `beforeAll` budget on a refused connection and FAILS. Only a TCP connect is
|
|
8
|
-
* asked — never a login — so no driver is needed here and no credential leaves the variable.
|
|
9
|
-
*/
|
|
10
|
-
export interface ProbeTarget {
|
|
11
|
-
host: string;
|
|
12
|
-
port: number;
|
|
13
|
-
}
|
|
1
|
+
import type { ProbeTarget } from './types.js';
|
|
14
2
|
/**
|
|
15
3
|
* Every `host:port` in the authority of a connection string, or `null` when the string names
|
|
16
4
|
* nothing a TCP connect can check — an SRV record, a unix socket, or a shape this cannot read.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reachability.d.ts","sourceRoot":"","sources":["../src/reachability.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reachability.d.ts","sourceRoot":"","sources":["../src/reachability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAiB7C;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAS,MAAM,eAAe,MAAM,KAAG,WAAW,EAAE,GAAG,IA6B/E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,YAAa,WAAW,EAAE,KAAG,MAAM,GAAG,IAkB9D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,aAAc,MAAM,OAAO,MAAM,eAAe,MAAM,KAAG,MAAM,GAAG,IAiB/F,CAAA"}
|
package/build/reachability.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { logger } from '@owlmeans/log';
|
|
3
|
+
import { CONNECT_TIMEOUT_MS, PROBE_SCRIPT, SPAWN_GRACE_MS } from './consts.local.js';
|
|
4
|
+
const log = logger('test-integration');
|
|
5
|
+
/**
|
|
6
|
+
* Whether the server a connection string names answers at all — asked synchronously, because
|
|
7
|
+
* Bun decides `test` vs `test.skip` synchronously and every gate is read at module scope.
|
|
8
|
+
*
|
|
9
|
+
* A populated variable says where the service should be, not that it is there: a developer's
|
|
10
|
+
* `.env` keeps the port-forward address long after the forward is gone, and a suite that trusts
|
|
11
|
+
* it spends its whole `beforeAll` budget on a refused connection and FAILS. Only a TCP connect is
|
|
12
|
+
* asked — never a login — so no driver is needed here and no credential leaves the variable.
|
|
13
|
+
*/
|
|
6
14
|
const answers = new Map();
|
|
7
15
|
const warned = new Set();
|
|
8
16
|
/**
|
|
@@ -39,29 +47,6 @@ export const parseTargets = (url, defaultPort) => {
|
|
|
39
47
|
}
|
|
40
48
|
return targets.length > 0 ? targets : null;
|
|
41
49
|
};
|
|
42
|
-
// Runs under whichever runtime runs the suite (`process.execPath`), so it is plain CommonJS
|
|
43
|
-
// that both `bun -e` and `node -e` evaluate.
|
|
44
|
-
const PROBE_SCRIPT = `
|
|
45
|
-
const net = require('net')
|
|
46
|
-
const [targets, timeout] = JSON.parse(process.env.OWLMEANS_TEST_PROBE)
|
|
47
|
-
let pending = targets.length
|
|
48
|
-
let last = ''
|
|
49
|
-
for (const target of targets) {
|
|
50
|
-
let settled = false
|
|
51
|
-
const socket = net.connect({ host: target.host, port: target.port })
|
|
52
|
-
const fail = code => {
|
|
53
|
-
if (settled) return
|
|
54
|
-
settled = true
|
|
55
|
-
socket.destroy()
|
|
56
|
-
last = code
|
|
57
|
-
if (--pending === 0) { process.stdout.write(last); process.exit(1) }
|
|
58
|
-
}
|
|
59
|
-
socket.setTimeout(timeout)
|
|
60
|
-
socket.once('connect', () => process.exit(0))
|
|
61
|
-
socket.once('timeout', () => fail('ETIMEDOUT'))
|
|
62
|
-
socket.once('error', error => fail(error.code || String(error)))
|
|
63
|
-
}
|
|
64
|
-
`;
|
|
65
50
|
/**
|
|
66
51
|
* `null` when at least one target accepts a TCP connection, otherwise the reason none did.
|
|
67
52
|
*
|
|
@@ -102,7 +87,7 @@ export const unreachableReason = (variable, url, defaultPort) => {
|
|
|
102
87
|
const reason = `${variable} is set, but ${unreachable} — start the service (or its port-forward) to run these specs`;
|
|
103
88
|
if (!warned.has(reason)) {
|
|
104
89
|
warned.add(reason);
|
|
105
|
-
|
|
90
|
+
log.warn('Skipping integration specs: a configured service is unreachable', { variable, unreachable });
|
|
106
91
|
}
|
|
107
92
|
return reason;
|
|
108
93
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reachability.js","sourceRoot":"","sources":["../src/reachability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"reachability.js","sourceRoot":"","sources":["../src/reachability.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface IntegrationGate<E> {
|
|
2
|
+
skip: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
env: Partial<E>;
|
|
5
|
+
}
|
|
6
|
+
export interface MongoEnv {
|
|
7
|
+
MONGO_URL: string;
|
|
8
|
+
MONGO_TEST_DB_PREFIX: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RedisEnv {
|
|
11
|
+
REDIS_URL: string;
|
|
12
|
+
REDIS_TEST_KEY_PREFIX: string;
|
|
13
|
+
}
|
|
14
|
+
export interface S3Env {
|
|
15
|
+
S3_ENDPOINT: string;
|
|
16
|
+
S3_KEY: string;
|
|
17
|
+
S3_SECRET: string;
|
|
18
|
+
S3_TEST_BUCKET: string;
|
|
19
|
+
S3_REGION: string;
|
|
20
|
+
}
|
|
21
|
+
export interface KubeEnv {
|
|
22
|
+
KUBE_CONFIG: string;
|
|
23
|
+
KUBE_TEST_OK: string;
|
|
24
|
+
}
|
|
25
|
+
export interface PostgresEnv {
|
|
26
|
+
POSTGRES_URL: string;
|
|
27
|
+
POSTGRES_TEST_DB_PREFIX: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SmtpEnv {
|
|
30
|
+
SMTP_HOST: string;
|
|
31
|
+
SMTP_PORT: string;
|
|
32
|
+
SMTP_SECURE: string;
|
|
33
|
+
SMTP_USER: string;
|
|
34
|
+
SMTP_PASSWORD: string;
|
|
35
|
+
SMTP_FROM: string;
|
|
36
|
+
/** Mailbox the specs deliver to. Required — these tests send real mail. */
|
|
37
|
+
SMTP_TEST_TO: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ProbeTarget {
|
|
40
|
+
host: string;
|
|
41
|
+
port: number;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,oBAAoB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,uBAAuB,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type CleanupFn = () => void | Promise<void>;
|
|
2
|
+
/**
|
|
3
|
+
* A connection-string variable whose server must answer a TCP connect for the gate to open.
|
|
4
|
+
*
|
|
5
|
+
* Only for a service a developer reaches through a local address that can go away — a
|
|
6
|
+
* port-forward, a container. A populated variable pointing at nothing then closes the gate with
|
|
7
|
+
* a printed reason instead of failing every suite behind it on a refused connection.
|
|
8
|
+
*/
|
|
9
|
+
export interface Reachable<E> {
|
|
10
|
+
key: keyof E & string;
|
|
11
|
+
defaultPort: number;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=types.local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.local.d.ts","sourceRoot":"","sources":["../src/types.local.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.local.js","sourceRoot":"","sources":["../src/types.local.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/test-integration",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.32",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@owlmeans/
|
|
24
|
+
"@owlmeans/log": "^0.1.18-rc.1",
|
|
25
|
+
"@owlmeans/test": "^0.1.18-rc.34"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@owlmeans/dep-config": "^0.1.18-rc.6",
|
|
28
|
-
"@types/bun": "^1.4.
|
|
29
|
+
"@types/bun": "^1.4.2",
|
|
29
30
|
"nodemon": "^3.1.14",
|
|
30
31
|
"typescript": "^7.0.2"
|
|
31
32
|
},
|
package/src/cleanup.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { logger } from '@owlmeans/log'
|
|
2
|
+
import type { CleanupFn } from './types.local.js'
|
|
3
|
+
|
|
4
|
+
const log = logger('test-integration')
|
|
2
5
|
|
|
3
6
|
const queue: CleanupFn[] = []
|
|
4
7
|
|
|
@@ -14,7 +17,7 @@ export const registerCleanup = (fn: CleanupFn): void => {
|
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Run all pending cleanup functions in reverse registration order.
|
|
17
|
-
* Errors are swallowed and
|
|
20
|
+
* Errors are swallowed and logged as warnings so a failing cleanup
|
|
18
21
|
* cannot mask a test failure.
|
|
19
22
|
*/
|
|
20
23
|
export const runCleanups = async (): Promise<void> => {
|
|
@@ -24,7 +27,7 @@ export const runCleanups = async (): Promise<void> => {
|
|
|
24
27
|
try {
|
|
25
28
|
await fn()
|
|
26
29
|
} catch (err) {
|
|
27
|
-
|
|
30
|
+
log.warn('Cleanup failed', err)
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** How long one connect may take before the address counts as unreachable. */
|
|
2
|
+
export const CONNECT_TIMEOUT_MS = 1_500
|
|
3
|
+
|
|
4
|
+
/** The probe runs in a child process; this covers its own startup on a loaded machine. */
|
|
5
|
+
export const SPAWN_GRACE_MS = 5_000
|
|
6
|
+
|
|
7
|
+
// Runs under whichever runtime runs the suite (`process.execPath`), so it is plain CommonJS
|
|
8
|
+
// that both `bun -e` and `node -e` evaluate.
|
|
9
|
+
export const PROBE_SCRIPT = `
|
|
10
|
+
const net = require('net')
|
|
11
|
+
const [targets, timeout] = JSON.parse(process.env.OWLMEANS_TEST_PROBE)
|
|
12
|
+
let pending = targets.length
|
|
13
|
+
let last = ''
|
|
14
|
+
for (const target of targets) {
|
|
15
|
+
let settled = false
|
|
16
|
+
const socket = net.connect({ host: target.host, port: target.port })
|
|
17
|
+
const fail = code => {
|
|
18
|
+
if (settled) return
|
|
19
|
+
settled = true
|
|
20
|
+
socket.destroy()
|
|
21
|
+
last = code
|
|
22
|
+
if (--pending === 0) { process.stdout.write(last); process.exit(1) }
|
|
23
|
+
}
|
|
24
|
+
socket.setTimeout(timeout)
|
|
25
|
+
socket.once('connect', () => process.exit(0))
|
|
26
|
+
socket.once('timeout', () => fail('ETIMEDOUT'))
|
|
27
|
+
socket.once('error', error => fail(error.code || String(error)))
|
|
28
|
+
}
|
|
29
|
+
`
|
package/src/gates.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { hasEnv, requireEnv } from '@owlmeans/test'
|
|
2
2
|
import { unreachableReason } from './reachability.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
skip: boolean
|
|
6
|
-
reason?: string
|
|
7
|
-
env: Partial<E>
|
|
8
|
-
}
|
|
3
|
+
import type { Reachable } from './types.local.js'
|
|
4
|
+
import type { IntegrationGate, KubeEnv, MongoEnv, PostgresEnv, RedisEnv, S3Env, SmtpEnv } from './types.js'
|
|
9
5
|
|
|
10
6
|
const collect = <E>(keys: (keyof E & string)[]): Partial<E> => {
|
|
11
7
|
const env: Record<string, string> = {}
|
|
@@ -15,18 +11,6 @@ const collect = <E>(keys: (keyof E & string)[]): Partial<E> => {
|
|
|
15
11
|
return env as Partial<E>
|
|
16
12
|
}
|
|
17
13
|
|
|
18
|
-
/**
|
|
19
|
-
* A connection-string variable whose server must answer a TCP connect for the gate to open.
|
|
20
|
-
*
|
|
21
|
-
* Only for a service a developer reaches through a local address that can go away — a
|
|
22
|
-
* port-forward, a container. A populated variable pointing at nothing then closes the gate with
|
|
23
|
-
* a printed reason instead of failing every suite behind it on a refused connection.
|
|
24
|
-
*/
|
|
25
|
-
interface Reachable<E> {
|
|
26
|
-
key: keyof E & string
|
|
27
|
-
defaultPort: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
14
|
const toIntegrationGate = <E>(
|
|
31
15
|
required: (keyof E & string)[],
|
|
32
16
|
optional: (keyof E & string)[] = [],
|
|
@@ -42,63 +26,24 @@ const toIntegrationGate = <E>(
|
|
|
42
26
|
return { skip: false, env }
|
|
43
27
|
}
|
|
44
28
|
|
|
45
|
-
export interface MongoEnv {
|
|
46
|
-
MONGO_URL: string
|
|
47
|
-
MONGO_TEST_DB_PREFIX: string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
29
|
export const mongoGate = (): IntegrationGate<MongoEnv> =>
|
|
51
30
|
toIntegrationGate<MongoEnv>(['MONGO_URL'], ['MONGO_TEST_DB_PREFIX'], { key: 'MONGO_URL', defaultPort: 27017 })
|
|
52
31
|
|
|
53
|
-
export interface RedisEnv {
|
|
54
|
-
REDIS_URL: string
|
|
55
|
-
REDIS_TEST_KEY_PREFIX: string
|
|
56
|
-
}
|
|
57
|
-
|
|
58
32
|
export const redisGate = (): IntegrationGate<RedisEnv> =>
|
|
59
33
|
toIntegrationGate<RedisEnv>(['REDIS_URL'], ['REDIS_TEST_KEY_PREFIX'], { key: 'REDIS_URL', defaultPort: 6379 })
|
|
60
34
|
|
|
61
|
-
export interface S3Env {
|
|
62
|
-
S3_ENDPOINT: string
|
|
63
|
-
S3_KEY: string
|
|
64
|
-
S3_SECRET: string
|
|
65
|
-
S3_TEST_BUCKET: string
|
|
66
|
-
S3_REGION: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
35
|
export const s3Gate = (): IntegrationGate<S3Env> =>
|
|
70
36
|
toIntegrationGate<S3Env>(
|
|
71
37
|
['S3_ENDPOINT', 'S3_KEY', 'S3_SECRET', 'S3_TEST_BUCKET'],
|
|
72
38
|
['S3_REGION']
|
|
73
39
|
)
|
|
74
40
|
|
|
75
|
-
export interface KubeEnv {
|
|
76
|
-
KUBE_CONFIG: string
|
|
77
|
-
KUBE_TEST_OK: string
|
|
78
|
-
}
|
|
79
|
-
|
|
80
41
|
export const kubeGate = (): IntegrationGate<KubeEnv> =>
|
|
81
42
|
toIntegrationGate<KubeEnv>(['KUBE_CONFIG', 'KUBE_TEST_OK'])
|
|
82
43
|
|
|
83
|
-
export interface PostgresEnv {
|
|
84
|
-
POSTGRES_URL: string
|
|
85
|
-
POSTGRES_TEST_DB_PREFIX: string
|
|
86
|
-
}
|
|
87
|
-
|
|
88
44
|
export const postgresGate = (): IntegrationGate<PostgresEnv> =>
|
|
89
45
|
toIntegrationGate<PostgresEnv>(['POSTGRES_URL'], ['POSTGRES_TEST_DB_PREFIX'], { key: 'POSTGRES_URL', defaultPort: 5432 })
|
|
90
46
|
|
|
91
|
-
export interface SmtpEnv {
|
|
92
|
-
SMTP_HOST: string
|
|
93
|
-
SMTP_PORT: string
|
|
94
|
-
SMTP_SECURE: string
|
|
95
|
-
SMTP_USER: string
|
|
96
|
-
SMTP_PASSWORD: string
|
|
97
|
-
SMTP_FROM: string
|
|
98
|
-
/** Mailbox the specs deliver to. Required — these tests send real mail. */
|
|
99
|
-
SMTP_TEST_TO: string
|
|
100
|
-
}
|
|
101
|
-
|
|
102
47
|
export const smtpGate = (): IntegrationGate<SmtpEnv> =>
|
|
103
48
|
toIntegrationGate<SmtpEnv>(
|
|
104
49
|
['SMTP_HOST', 'SMTP_USER', 'SMTP_PASSWORD', 'SMTP_FROM', 'SMTP_TEST_TO'],
|
package/src/index.ts
CHANGED
package/src/reachability.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { logger } from '@owlmeans/log'
|
|
3
|
+
import { CONNECT_TIMEOUT_MS, PROBE_SCRIPT, SPAWN_GRACE_MS } from './consts.local.js'
|
|
4
|
+
import type { ProbeTarget } from './types.js'
|
|
5
|
+
|
|
6
|
+
const log = logger('test-integration')
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Whether the server a connection string names answers at all — asked synchronously, because
|
|
@@ -10,17 +15,6 @@ import { spawnSync } from 'node:child_process'
|
|
|
10
15
|
* asked — never a login — so no driver is needed here and no credential leaves the variable.
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
|
-
export interface ProbeTarget {
|
|
14
|
-
host: string
|
|
15
|
-
port: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** How long one connect may take before the address counts as unreachable. */
|
|
19
|
-
const CONNECT_TIMEOUT_MS = 1_500
|
|
20
|
-
|
|
21
|
-
/** The probe runs in a child process; this covers its own startup on a loaded machine. */
|
|
22
|
-
const SPAWN_GRACE_MS = 5_000
|
|
23
|
-
|
|
24
18
|
const answers = new Map<string, string | null>()
|
|
25
19
|
const warned = new Set<string>()
|
|
26
20
|
|
|
@@ -60,30 +54,6 @@ export const parseTargets = (url: string, defaultPort: number): ProbeTarget[] |
|
|
|
60
54
|
return targets.length > 0 ? targets : null
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
// Runs under whichever runtime runs the suite (`process.execPath`), so it is plain CommonJS
|
|
64
|
-
// that both `bun -e` and `node -e` evaluate.
|
|
65
|
-
const PROBE_SCRIPT = `
|
|
66
|
-
const net = require('net')
|
|
67
|
-
const [targets, timeout] = JSON.parse(process.env.OWLMEANS_TEST_PROBE)
|
|
68
|
-
let pending = targets.length
|
|
69
|
-
let last = ''
|
|
70
|
-
for (const target of targets) {
|
|
71
|
-
let settled = false
|
|
72
|
-
const socket = net.connect({ host: target.host, port: target.port })
|
|
73
|
-
const fail = code => {
|
|
74
|
-
if (settled) return
|
|
75
|
-
settled = true
|
|
76
|
-
socket.destroy()
|
|
77
|
-
last = code
|
|
78
|
-
if (--pending === 0) { process.stdout.write(last); process.exit(1) }
|
|
79
|
-
}
|
|
80
|
-
socket.setTimeout(timeout)
|
|
81
|
-
socket.once('connect', () => process.exit(0))
|
|
82
|
-
socket.once('timeout', () => fail('ETIMEDOUT'))
|
|
83
|
-
socket.once('error', error => fail(error.code || String(error)))
|
|
84
|
-
}
|
|
85
|
-
`
|
|
86
|
-
|
|
87
57
|
/**
|
|
88
58
|
* `null` when at least one target accepts a TCP connection, otherwise the reason none did.
|
|
89
59
|
*
|
|
@@ -128,7 +98,7 @@ export const unreachableReason = (variable: string, url: string, defaultPort: nu
|
|
|
128
98
|
const reason = `${variable} is set, but ${unreachable} — start the service (or its port-forward) to run these specs`
|
|
129
99
|
if (!warned.has(reason)) {
|
|
130
100
|
warned.add(reason)
|
|
131
|
-
|
|
101
|
+
log.warn('Skipping integration specs: a configured service is unreachable', { variable, unreachable })
|
|
132
102
|
}
|
|
133
103
|
|
|
134
104
|
return reason
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type CleanupFn = () => void | Promise<void>
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A connection-string variable whose server must answer a TCP connect for the gate to open.
|
|
5
|
+
*
|
|
6
|
+
* Only for a service a developer reaches through a local address that can go away — a
|
|
7
|
+
* port-forward, a container. A populated variable pointing at nothing then closes the gate with
|
|
8
|
+
* a printed reason instead of failing every suite behind it on a refused connection.
|
|
9
|
+
*/
|
|
10
|
+
export interface Reachable<E> {
|
|
11
|
+
key: keyof E & string
|
|
12
|
+
defaultPort: number
|
|
13
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface IntegrationGate<E> {
|
|
2
|
+
skip: boolean
|
|
3
|
+
reason?: string
|
|
4
|
+
env: Partial<E>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface MongoEnv {
|
|
8
|
+
MONGO_URL: string
|
|
9
|
+
MONGO_TEST_DB_PREFIX: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RedisEnv {
|
|
13
|
+
REDIS_URL: string
|
|
14
|
+
REDIS_TEST_KEY_PREFIX: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface S3Env {
|
|
18
|
+
S3_ENDPOINT: string
|
|
19
|
+
S3_KEY: string
|
|
20
|
+
S3_SECRET: string
|
|
21
|
+
S3_TEST_BUCKET: string
|
|
22
|
+
S3_REGION: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface KubeEnv {
|
|
26
|
+
KUBE_CONFIG: string
|
|
27
|
+
KUBE_TEST_OK: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PostgresEnv {
|
|
31
|
+
POSTGRES_URL: string
|
|
32
|
+
POSTGRES_TEST_DB_PREFIX: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SmtpEnv {
|
|
36
|
+
SMTP_HOST: string
|
|
37
|
+
SMTP_PORT: string
|
|
38
|
+
SMTP_SECURE: string
|
|
39
|
+
SMTP_USER: string
|
|
40
|
+
SMTP_PASSWORD: string
|
|
41
|
+
SMTP_FROM: string
|
|
42
|
+
/** Mailbox the specs deliver to. Required — these tests send real mail. */
|
|
43
|
+
SMTP_TEST_TO: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ProbeTarget {
|
|
47
|
+
host: string
|
|
48
|
+
port: number
|
|
49
|
+
}
|