@strav/testing 1.0.0-alpha.24 → 1.0.0-alpha.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strav/testing",
3
- "version": "1.0.0-alpha.24",
3
+ "version": "1.0.0-alpha.25",
4
4
  "description": "Strav testing utilities — in-memory stream, typed fetch stub, Postgres availability probe + schema reset, bootTestApp orchestrator, stub providers.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -8,6 +8,7 @@
8
8
  "exports": {
9
9
  ".": "./src/index.ts",
10
10
  "./brain": "./src/brain/index.ts",
11
+ "./cache": "./src/cache/index.ts",
11
12
  "./postgres": "./src/postgres/index.ts"
12
13
  },
13
14
  "files": [
@@ -21,11 +22,11 @@
21
22
  "access": "public"
22
23
  },
23
24
  "dependencies": {
24
- "@strav/database": "1.0.0-alpha.24",
25
- "@strav/kernel": "1.0.0-alpha.24"
25
+ "@strav/database": "1.0.0-alpha.25",
26
+ "@strav/kernel": "1.0.0-alpha.25"
26
27
  },
27
28
  "peerDependencies": {
28
- "@strav/brain": "1.0.0-alpha.24",
29
+ "@strav/brain": "1.0.0-alpha.25",
29
30
  "@types/bun": ">=1.3.14"
30
31
  },
31
32
  "peerDependenciesMeta": {
@@ -0,0 +1,2 @@
1
+ export { isMemcachedAvailable } from './is_memcached_available.ts'
2
+ export { isRedisAvailable } from './is_redis_available.ts'
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Cheap connection probe over Bun's TCP socket — opens a connection,
3
+ * sends `version\r\n`, expects a `VERSION ...\r\n` reply. Cached for
4
+ * the lifetime of the test process.
5
+ *
6
+ * Returns `false` if `MEMCACHED_HOST` / `MEMCACHED_PORT` are missing
7
+ * OR the probe fails. Pair with
8
+ * `describe.skipIf(!await isMemcachedAvailable())`.
9
+ */
10
+
11
+ let cachedAvailability: boolean | null = null
12
+
13
+ export async function isMemcachedAvailable(): Promise<boolean> {
14
+ if (cachedAvailability !== null) return cachedAvailability
15
+ const host = process.env['MEMCACHED_HOST']
16
+ const portStr = process.env['MEMCACHED_PORT']
17
+ if (host === undefined || host === '' || portStr === undefined || portStr === '') {
18
+ cachedAvailability = false
19
+ return false
20
+ }
21
+ const port = Number(portStr)
22
+ if (!Number.isFinite(port) || port <= 0) {
23
+ cachedAvailability = false
24
+ return false
25
+ }
26
+ try {
27
+ cachedAvailability = await probe(host, port)
28
+ } catch {
29
+ cachedAvailability = false
30
+ }
31
+ return cachedAvailability
32
+ }
33
+
34
+ function probe(host: string, port: number): Promise<boolean> {
35
+ return new Promise((resolve) => {
36
+ let settled = false
37
+ const finish = (ok: boolean): void => {
38
+ if (settled) return
39
+ settled = true
40
+ resolve(ok)
41
+ }
42
+ const timeout = setTimeout(() => finish(false), 2_000)
43
+ void Bun.connect({
44
+ hostname: host,
45
+ port,
46
+ socket: {
47
+ open(socket) {
48
+ socket.write('version\r\n')
49
+ },
50
+ data(socket, chunk) {
51
+ const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
52
+ const text = new TextDecoder().decode(bytes)
53
+ const ok = text.startsWith('VERSION')
54
+ // Settle before closing — `socket.end()` synchronously fires
55
+ // the `close` callback, which would beat `finish(ok)` to the
56
+ // settle line and report a false negative.
57
+ clearTimeout(timeout)
58
+ finish(ok)
59
+ socket.end()
60
+ },
61
+ error(_socket, _error) {
62
+ clearTimeout(timeout)
63
+ finish(false)
64
+ },
65
+ close() {
66
+ clearTimeout(timeout)
67
+ if (!settled) finish(false)
68
+ },
69
+ },
70
+ }).catch(() => {
71
+ clearTimeout(timeout)
72
+ finish(false)
73
+ })
74
+ })
75
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Cheap connection probe — opens `Bun.RedisClient` against
3
+ * `REDIS_URL`, sends `PING`, reports. Cached for the lifetime of the
4
+ * test process.
5
+ *
6
+ * Returns `false` if `REDIS_URL` is missing OR the connection / PING
7
+ * fails. Pair with `describe.skipIf(!await isRedisAvailable())`.
8
+ */
9
+
10
+ import { RedisClient } from 'bun'
11
+
12
+ let cachedAvailability: boolean | null = null
13
+
14
+ export async function isRedisAvailable(): Promise<boolean> {
15
+ if (cachedAvailability !== null) return cachedAvailability
16
+ const url = process.env['REDIS_URL']
17
+ if (url === undefined || url === '') {
18
+ cachedAvailability = false
19
+ return false
20
+ }
21
+ let client: RedisClient | undefined
22
+ try {
23
+ client = new RedisClient(url)
24
+ // `send('PING', [])` is supported on every Bun.RedisClient build —
25
+ // safer than `ping()` which isn't on the typed surface.
26
+ const reply = await client.send('PING', [])
27
+ cachedAvailability = reply === 'PONG' || reply === 'OK' || typeof reply === 'string'
28
+ return cachedAvailability
29
+ } catch {
30
+ cachedAvailability = false
31
+ return false
32
+ } finally {
33
+ try {
34
+ client?.close()
35
+ } catch {
36
+ // Already closed / never connected — nothing to clean.
37
+ }
38
+ }
39
+ }
package/src/index.ts CHANGED
@@ -27,3 +27,7 @@ export {
27
27
  resetSchema,
28
28
  testDatabaseUrl,
29
29
  } from './postgres/index.ts'
30
+
31
+ // Cache availability probes — also re-exported under
32
+ // `@strav/testing/cache` for the same reason.
33
+ export { isMemcachedAvailable, isRedisAvailable } from './cache/index.ts'