@devopsplaybook.io/common-utils 1.10.1-beta.23.b1906d7 → 1.11.0-beta.24.d8f9aef

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 (69) hide show
  1. package/README.md +73 -27
  2. package/dist/src/ConfigBase.d.ts +31 -0
  3. package/dist/src/ConfigBase.js +58 -16
  4. package/dist/src/DbUtils.d.ts +20 -3
  5. package/dist/src/DbUtils.js +93 -2
  6. package/dist/src/DbUtilsNoTelemetry.d.ts +4 -1
  7. package/dist/src/DbUtilsNoTelemetry.js +62 -6
  8. package/dist/src/PostgresDbUtils.d.ts +41 -10
  9. package/dist/src/PostgresDbUtils.js +357 -304
  10. package/dist/src/SqlDbUtils.d.ts +12 -4
  11. package/dist/src/SqlDbUtils.js +76 -30
  12. package/dist/src/users/Auth.d.ts +11 -1
  13. package/dist/src/users/Auth.js +160 -44
  14. package/dist/src/users/User.d.ts +10 -0
  15. package/dist/src/users/User.js +30 -10
  16. package/dist/src/users/UserApiToken.d.ts +4 -0
  17. package/dist/src/users/UserApiToken.js +11 -0
  18. package/dist/src/users/UsersApiTokensData.d.ts +12 -0
  19. package/dist/src/users/UsersApiTokensData.js +130 -33
  20. package/dist/src/users/UsersData.d.ts +20 -1
  21. package/dist/src/users/UsersData.js +150 -52
  22. package/dist/src/users/UsersRoutes.js +178 -61
  23. package/dist/src/users/index.d.ts +8 -0
  24. package/dist/src/users/index.js +24 -0
  25. package/package.json +58 -1
  26. package/.github/workflows/main-build.yml +0 -18
  27. package/.github/workflows/pr-check.yml +0 -27
  28. package/.github/workflows/reusable-merge-build.yml +0 -197
  29. package/.github/workflows/reusable-npm-merge.yml +0 -135
  30. package/.github/workflows/reusable-npm-pr.yml +0 -183
  31. package/.github/workflows/reusable-npm-upgrade.yml +0 -92
  32. package/.github/workflows/reusable-pr-verify.yml +0 -181
  33. package/AGENTS.md +0 -105
  34. package/index.ts +0 -18
  35. package/jest.config.js +0 -17
  36. package/prettierrc.json +0 -5
  37. package/src/ConfigBase.spec.ts +0 -108
  38. package/src/ConfigBase.ts +0 -297
  39. package/src/DbUtils.spec.ts +0 -23
  40. package/src/DbUtils.ts +0 -116
  41. package/src/DbUtilsNoTelemetry.spec.ts +0 -168
  42. package/src/DbUtilsNoTelemetry.ts +0 -117
  43. package/src/LLM.spec.ts +0 -303
  44. package/src/LLM.ts +0 -204
  45. package/src/Notifications.spec.ts +0 -265
  46. package/src/Notifications.ts +0 -201
  47. package/src/OTelContext.spec.ts +0 -58
  48. package/src/OTelContext.ts +0 -63
  49. package/src/PostgresDbUtils.spec.ts +0 -153
  50. package/src/PostgresDbUtils.ts +0 -666
  51. package/src/SqlDbUtils.spec.ts +0 -108
  52. package/src/SqlDbUtils.ts +0 -152
  53. package/src/SystemCommand.spec.ts +0 -18
  54. package/src/SystemCommand.ts +0 -23
  55. package/src/Timeout.spec.ts +0 -18
  56. package/src/Timeout.ts +0 -12
  57. package/src/users/Auth.spec.ts +0 -268
  58. package/src/users/Auth.ts +0 -202
  59. package/src/users/User.ts +0 -75
  60. package/src/users/UserApiToken.ts +0 -55
  61. package/src/users/UserPassword.spec.ts +0 -28
  62. package/src/users/UserPassword.ts +0 -20
  63. package/src/users/UserSession.ts +0 -9
  64. package/src/users/UsersApiTokensData.spec.ts +0 -158
  65. package/src/users/UsersApiTokensData.ts +0 -125
  66. package/src/users/UsersData.ts +0 -141
  67. package/src/users/UsersRoutes.ts +0 -374
  68. package/tsconfig.json +0 -15
  69. package/tsconfig.spec.json +0 -8
@@ -1,181 +0,0 @@
1
- name: "Reusable: PR Verify"
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- docker_platforms:
7
- description: "Docker platforms to build for"
8
- required: false
9
- type: string
10
- default: "linux/arm64/v8,linux/amd64"
11
- node_app_directories:
12
- description: "JSON array of Node.js app directories to build, lint, and test"
13
- required: false
14
- type: string
15
- default: ""
16
- node_version:
17
- description: "Node.js version to use"
18
- required: false
19
- type: string
20
- default: "22"
21
- secrets:
22
- DOCKER_HUB_USERNAME:
23
- required: true
24
- DOCKER_HUB_ACCESS_TOKEN:
25
- required: true
26
- QUALITY_DASHBOARD_TOKEN:
27
- required: false
28
- QUALITY_DASHBOARD_URL:
29
- required: false
30
-
31
- jobs:
32
- version-check:
33
- runs-on: ubuntu-latest
34
- steps:
35
- - uses: actions/checkout@v7
36
-
37
- - name: Fetch main branch
38
- run: git fetch origin main --depth=1
39
-
40
- - name: Verify version is higher than main
41
- run: |
42
- PR_VERSION=$(jq -r '.version' package.json)
43
- MAIN_VERSION=$(git show origin/main:package.json | jq -r '.version')
44
-
45
- echo "PR version: ${PR_VERSION}"
46
- echo "Main version: ${MAIN_VERSION}"
47
-
48
- if [ "$PR_VERSION" = "$MAIN_VERSION" ]; then
49
- echo "::error::PR version (${PR_VERSION}) must be higher than main branch version (${MAIN_VERSION}). Please bump the version in package.json."
50
- exit 1
51
- fi
52
-
53
- HIGHER=$(printf '%s\n%s' "$PR_VERSION" "$MAIN_VERSION" | sort -V | tail -n1)
54
- if [ "$HIGHER" != "$PR_VERSION" ]; then
55
- echo "::error::PR version (${PR_VERSION}) is lower than main branch version (${MAIN_VERSION}). Please bump the version in package.json."
56
- exit 1
57
- fi
58
-
59
- echo "Version check passed: ${PR_VERSION} > ${MAIN_VERSION}"
60
-
61
- node-build:
62
- needs: version-check
63
- if: inputs.node_app_directories != ''
64
- strategy:
65
- matrix:
66
- app: ${{ fromJSON(inputs.node_app_directories) }}
67
- runs-on: ubuntu-latest
68
- defaults:
69
- run:
70
- working-directory: ${{ matrix.app }}
71
- steps:
72
- - uses: actions/checkout@v7
73
-
74
- - name: Setup Node.js
75
- uses: actions/setup-node@v7
76
- with:
77
- node-version: ${{ inputs.node_version }}
78
- cache: "npm"
79
- cache-dependency-path: ${{ matrix.app }}/package-lock.json
80
-
81
- - name: Install dependencies
82
- run: npm ci
83
-
84
- - name: Build
85
- run: npm run build
86
-
87
- - name: Lint
88
- run: npm run lint
89
-
90
- - name: Test
91
- run: npm run test
92
-
93
- - name: Zip coverage and upload to Quality Dashboard
94
- if: always()
95
- env:
96
- QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
97
- QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
98
- run: |
99
- APP_NAME="${{ matrix.app }}"
100
- PACKAGE_NAME=$(node -p "require('./package.json').name")
101
- REPORT_KEY="$(echo "$APP_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')_coverage"
102
-
103
- if [ -d coverage ]; then
104
- if [ -z "$QUALITY_DASHBOARD_URL" ] || [ -z "$QUALITY_DASHBOARD_TOKEN" ]; then
105
- echo "Quality Dashboard URL or token not set, skipping upload"
106
- exit 0
107
- fi
108
- # Zip coverage contents at root level so the jest processor
109
- # finds coverage-final.json / clover.xml directly
110
- (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
111
-
112
- META=$(jq -n \
113
- --arg key "$REPORT_KEY" \
114
- --arg pkg "$PACKAGE_NAME" \
115
- '{
116
- key: $key,
117
- displayName: "Coverage: \($pkg)",
118
- processor: "jest"
119
- }')
120
-
121
- echo "Uploading coverage: $REPORT_KEY"
122
- HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
123
- -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
124
- -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
125
- -F "meta=$META" \
126
- -F "file=@${{ runner.temp }}/coverage.zip" \
127
- --max-time 30)
128
-
129
- if [ "$HTTP_CODE" = "201" ]; then
130
- echo "\u2713 Coverage uploaded ($HTTP_CODE)"
131
- else
132
- echo "\u26a0 Upload returned HTTP $HTTP_CODE"
133
- fi
134
- else
135
- echo "No coverage/ directory found, skipping upload"
136
- fi
137
-
138
- docker-build:
139
- needs: version-check
140
- runs-on: ubuntu-latest
141
- steps:
142
- - uses: actions/checkout@v7
143
-
144
- - name: Set up QEMU
145
- uses: docker/setup-qemu-action@v4
146
-
147
- - name: Set up Docker Buildx
148
- uses: docker/setup-buildx-action@v4
149
-
150
- - name: Login to Docker Hub
151
- uses: docker/login-action@v4
152
- with:
153
- username: ${{ secrets.DOCKER_HUB_USERNAME }}
154
- password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
155
-
156
- - name: Build and Push Docker Image
157
- env:
158
- DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
159
- DOCKER_PLATFORMS: ${{ inputs.docker_platforms }}
160
- PR_NUMBER: ${{ github.event.pull_request.number }}
161
- run: |
162
- set -e
163
- SERVICE_NAME=$(jq -r '.name' package.json)
164
- IMAGE="${DOCKER_HUB_USERNAME}/${SERVICE_NAME}"
165
-
166
- # `beta-pr-<number>` is the immutable per-PR reference: it identifies
167
- # exactly the artifact validated by this pull request, and the merge
168
- # build promotes it as-is to the semantic version tags.
169
- PR_TAG="${PR_NUMBER}"
170
- if [ -z "${PR_TAG}" ]; then
171
- PR_TAG="manual-${GITHUB_SHA::7}"
172
- fi
173
-
174
- echo "Building ${IMAGE}:beta-pr-${PR_TAG} and ${IMAGE}:beta"
175
- docker buildx build \
176
- --platform "${DOCKER_PLATFORMS}" \
177
- --push \
178
- -f Dockerfile \
179
- -t "${IMAGE}:beta-pr-${PR_TAG}" \
180
- -t "${IMAGE}:beta" \
181
- .
package/AGENTS.md DELETED
@@ -1,105 +0,0 @@
1
- # AGENTS.md
2
-
3
- This file provides context for AI coding agents working on this repository.
4
-
5
- ## Repository Overview
6
-
7
- `@devopsplaybook.io/common-utils` is a shared npm library that centralizes utility modules used across devopsplaybook.io server projects. It serves two purposes:
8
-
9
- 1. **Node.js library** (`src/`): Database access (SQLite via `better-sqlite3`, PostgreSQL via `pg.Pool`), configuration loading, OpenTelemetry context management, and small helpers.
10
- 2. **Reusable GitHub Actions workflows** (`.github/workflows/reusable-*.yml`): Standardized CI/CD pipelines adopted by all projects in the organization.
11
-
12
- ## Architecture
13
-
14
- ```
15
- index.ts # Barrel re-exports for all modules
16
- src/
17
- OTelContext.ts # createOTelContext() factory (tracer/meter/logger singletons)
18
- ConfigBase.ts # Abstract base class for 3-layer config (env > config.json > defaults)
19
- SqlDbUtils.ts # SQLite operations (better-sqlite3, synchronous)
20
- PostgresDbUtils.ts # PostgreSQL operations (pg.Pool, async/Promise)
21
- DbUtils.ts # Unified facade dispatching to Sql or Postgres
22
- DbUtilsNoTelemetry.ts # Same DB ops without OTel span overhead
23
- users/ # Auth & user management module set
24
- User.ts # User model, roles and application-defined scopes
25
- UserSession.ts # Decoded JWT session
26
- UserApiToken.ts # User API token model (SHA-256 hash only)
27
- Auth.ts # JWT auth (key init, guards, session decode) + API token resolution
28
- UserPassword.ts # bcrypt password hashing/verification
29
- UsersData.ts # Users table CRUD (SQLite/Postgres)
30
- UsersApiTokensData.ts # users_api_tokens table CRUD (SQLite/Postgres)
31
- UsersRoutes.ts # Standard fastify user management routes (incl. self-service API tokens)
32
- SystemCommand.ts # Promise wrapper around child_process.exec
33
- Timeout.ts # Promise wrapper around setTimeout
34
- LLM.ts # OpenAI-compatible chat completions client (LLMClient)
35
- *.spec.ts # Co-located test files
36
- .github/workflows/
37
- main-build.yml # Caller: push to main -> reusable-npm-merge
38
- pr-check.yml # Caller: PR to main -> reusable-npm-pr
39
- reusable-npm-merge.yml # Lint + test + build + publish release to npm
40
- reusable-npm-pr.yml # Lint + test + build + publish beta tag + comment PR
41
- reusable-npm-upgrade.yml # npm-check-updates + auto PR (manual, workflow_call)
42
- reusable-pr-verify.yml # Matrix Node.js + multi-platform Docker build -> beta-pr-<PR> and beta
43
- reusable-merge-build.yml # Promotes the PR image to the version tags on merge (no build/lint/test)
44
- ```
45
-
46
- ## Weekly Dependency Updates
47
-
48
- The scheduled `npm-upgrade.yml` caller was retired: it created dependency upgrade pull requests every week that no one merged. `reusable-npm-upgrade.yml` is kept for manual, on-demand upgrades (`workflow_call`; adopting repositories can trigger it through their own `workflow_dispatch` caller).
49
-
50
- The weekly update of the shared npm libraries is agent-driven instead: a Kubernetes CronJob (`planner-shared-libraries-update` in the `DidierHoarau/didier-home` GitOps repository) creates a Planner task in the `Projects/xMaintenance` project every Friday, and the planner-llm-agent executes the `update-shared-libraries` skill from `DidierHoarau/planner-llm-agent-config`. The skill updates `otel-utils` first, then `otel-utils-fastify` and `common-utils` once the freshly published `otel-utils` version is visible on npm, then the consumer repositories discovered with GitHub code search. It applies minor/patch updates only (majors are reported, never applied), skips repositories already current against the npm registry, and delivers one squash-merged pull request per repository after green checks. Do not reintroduce a scheduled upgrade workflow in this repository: it would compete with the agent-driven update.
51
-
52
- ## Key Conventions
53
-
54
- - **TypeScript**: Target ES2019, CommonJS output, strict mode enabled, declarations generated.
55
- - **Linting**: Uses `oxlint` with its default **recommended** preset. Minimize disable comments (`eslint-disable`-style comments are honored by oxlint); use them only when the rule cannot be satisfied (e.g. `no-explicit-any` is not enabled by the recommended preset, so `any` needs no directive).
56
- - **Tests**: Jest with `@swc/jest` transform (`jsc.target` matches tsconfig ES2019) and `v8` coverage provider. Spec files live next to source (`*.spec.ts`). Run with `npm run test`. The `tsconfig.spec.json` includes jest types; `npm run build` also type-checks specs (`tsc -p tsconfig.spec.json --noEmit`).
57
- - **No default exports**: All modules use named exports only.
58
- - **OTel dependency injection**: Every DB module exposes a `*SetOTel(tracer, logger)` function that must be called before `*Init()`. OTel instances are stored as module-level singletons.
59
- - **Auth modules**: `AuthSetOTel(tracer)` and `UsersDataSetOTel(tracer)` must be called before `AuthInit`. Application scopes are registered through `AuthInit(context, config, allScopes)`; `UsersRoutes` relies on `req.tracerSpanApi` set by the `otel-utils-fastify` hooks.
60
- - **ModuleLogger pattern**: `StandardLogger` only exposes `createModuleLogger(name)`. DB modules call `logger.createModuleLogger("ModuleName")` internally. Never call `.info()` or `.error()` directly on a `StandardLogger`.
61
- - **SQLite-first SQL**: Write SQL with `?` placeholders. The `DbUtils` facade and `DbUtilsNoTelemetry` module auto-convert to `$1, $2, ...` for Postgres via `convertToPostgresPlaceholders()`.
62
- - **Migration convention**: SQL files named `init-NNNN.sql`. `init-0000.sql` must create the `metadata` table. Subsequent files are applied in lexicographic order; applied versions are tracked in `metadata` for idempotency.
63
- - **Immutable Docker builds**: images are built once on the pull request (`reusable-pr-verify.yml` pushes `beta-pr-<PR number>` and `beta`) and promoted unchanged on merge (`reusable-merge-build.yml` writes `<version>`, `<major>`, `<minor>` and `latest`). The merge workflow runs no build, lint or test: it resolves the merged PR, carbon-copies the manifest with `docker buildx imagetools create --prefer-index=false`, then reads back each published digest and fails on a mismatch. It falls back to a full build only when no `beta-pr-<PR number>` image can be promoted. Image name and version come from the root `package.json`, and PR branches must be up to date with the default branch before merging.
64
- - **Reusable workflow permissions ceiling**: `reusable-merge-build.yml` may request only `contents: read`. A called workflow cannot exceed its caller's permissions, and callers use the default read-only token whose ceiling is `contents: read, pull-requests: none`. Adding `pull-requests: read` does not fail here — it fails in every consuming repo at startup with `Invalid workflow file ... requesting 'pull-requests: read', but is only allowed 'pull-requests: none'`. `GET /repos/{owner}/{repo}/commits/{sha}/pulls` works with `contents: read`, so no extra scope is needed.
65
-
66
- ## Build and Verification
67
-
68
- ```bash
69
- npm install
70
- npm run build # tsc -> dist/
71
- npm run lint # oxlint src (must pass with 0 errors)
72
- npm run test # jest --coverage (all tests must pass)
73
- ```
74
-
75
- All three commands must pass before committing. The CI pipeline (`reusable-npm-merge.yml`) runs the same checks.
76
-
77
- ## Dependencies
78
-
79
- | Package | Role |
80
- | ------------------------------- | ------------------------------------------------------------------------------------------ |
81
- | `@devopsplaybook.io/otel-utils` | `StandardTracer`, `StandardLogger`, `StandardMeter`, `ModuleLogger`, `ConfigOTelInterface` |
82
- | `better-sqlite3` | Synchronous SQLite driver (NOT the callback-based `sqlite3`) |
83
- | `pg` | PostgreSQL client with connection pooling |
84
- | `uuid` | v14+ (ESM -- requires `jest.mock("uuid")` in tests) |
85
- | `fs-extra` | Async/sync file operations, `readJson`/`ensureDir` |
86
- | `bcrypt` | Password hashing (users module) |
87
- | `jsonwebtoken` | JWT signing/verification (auth module) |
88
- | `fastify` | HTTP framework types used by `UsersRoutes` |
89
-
90
- ## Known Gotchas
91
-
92
- - **uuid ESM**: `uuid` v14+ ships ESM. In any test that transitively imports `uuid`, add `jest.mock("uuid", () => ({ v4: () => "mock-uuid-1234" }))` to avoid `SyntaxError: Unexpected token 'export'`.
93
- - **better-sqlite3 is synchronous**: `SqlDbUtils` functions return values directly (not Promises). `PostgresDbUtils` functions return Promises. The `DbUtils` facade returns `number | Promise<number>` depending on the active backend.
94
- - **Disable-comment placement**: `eslint-disable-next-line` applies to the **immediately following line only** (oxlint honors these comments too). When disabling a rule inside a function call argument, place the comment directly before the offending expression, not before the function call.
95
- - **pg callback typing**: Always explicitly type pg callback parameters: `(error: Error | null, result: { rowCount: number | null })`. TypeScript cannot infer these from the overloaded `pool.query` signature.
96
-
97
- ## Adopting in Other Projects
98
-
99
- See the [README](./README.md) for full adoption instructions. The typical pattern:
100
-
101
- 1. Add `@devopsplaybook.io/common-utils` as a dependency.
102
- 2. Replace the project's `OTelContext.ts` with `createOTelContext()`.
103
- 3. Replace the project's `Config` class to `extend ConfigBase`.
104
- 4. Replace DB utility files with re-exports from `common-utils`.
105
- 5. Update `App.ts` to call `*SetOTel()` and `*Init()` from `common-utils`.
package/index.ts DELETED
@@ -1,18 +0,0 @@
1
- export * from "./src/OTelContext";
2
- export * from "./src/ConfigBase";
3
- export * from "./src/DbUtils";
4
- export * from "./src/DbUtilsNoTelemetry";
5
- export * from "./src/SqlDbUtils";
6
- export * from "./src/PostgresDbUtils";
7
- export * from "./src/Notifications";
8
- export * from "./src/LLM";
9
- export * from "./src/SystemCommand";
10
- export * from "./src/Timeout";
11
- export * from "./src/users/User";
12
- export * from "./src/users/UserSession";
13
- export * from "./src/users/UserApiToken";
14
- export * from "./src/users/Auth";
15
- export * from "./src/users/UserPassword";
16
- export * from "./src/users/UsersData";
17
- export * from "./src/users/UsersApiTokensData";
18
- export * from "./src/users/UsersRoutes";
package/jest.config.js DELETED
@@ -1,17 +0,0 @@
1
- module.exports = {
2
- moduleFileExtensions: ["ts", "js"],
3
- transform: {
4
- "^.+\\.(ts|tsx)$": [
5
- "@swc/jest",
6
- {
7
- jsc: {
8
- target: "es2019",
9
- },
10
- },
11
- ],
12
- },
13
- coverageProvider: "v8",
14
- testMatch: ["/**/src/**/*.spec.(ts|js)"],
15
- testPathIgnorePatterns: ["/node_modules/", "/dist/"],
16
- testEnvironment: "node",
17
- };
package/prettierrc.json DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "tabWidth": 2,
3
- "semi": true,
4
- "singleQuote": false
5
- }
@@ -1,108 +0,0 @@
1
- import * as fse from "fs-extra";
2
- import * as path from "path";
3
- import * as os from "os";
4
-
5
- jest.mock("uuid", () => ({
6
- v4: () => "mock-uuid-1234",
7
- }));
8
-
9
- import { ConfigBase } from "./ConfigBase";
10
-
11
- /** Concrete subclass for testing. */
12
- class TestConfig extends ConfigBase {
13
- public MY_SETTING = "default_value";
14
- public SECRET_KEY = "secret_default";
15
-
16
- constructor(configFile?: string) {
17
- super("test-service", configFile);
18
- this.addConfigField({ field: "MY_SETTING" });
19
- this.addConfigField({ field: "SECRET_KEY", sensitive: true });
20
- }
21
- }
22
-
23
- describe("ConfigBase", () => {
24
- let tmpDir: string;
25
- let configPath: string;
26
-
27
- beforeEach(() => {
28
- tmpDir = fse.mkdtempSync(path.join(os.tmpdir(), "config-test-"));
29
- configPath = path.join(tmpDir, "config.json");
30
- });
31
-
32
- afterEach(() => {
33
- fse.removeSync(tmpDir);
34
- // Clean up env vars that tests may have set
35
- delete process.env.MY_SETTING;
36
- delete process.env.SECRET_KEY;
37
- delete process.env.LOG_LEVEL;
38
- delete process.env.DATA_DIR;
39
- });
40
-
41
- it("should initialise with default values", () => {
42
- const config = new TestConfig(configPath);
43
- expect(config.SERVICE_ID).toBe("test-service");
44
- expect(config.MY_SETTING).toBe("default_value");
45
- expect(config.SECRET_KEY).toBe("secret_default");
46
- expect(config.LOG_LEVEL).toBe("info");
47
- expect(config.DATABASE_TYPE).toBe("sqlite");
48
- });
49
-
50
- it("should load values from config.json", async () => {
51
- fse.writeJsonSync(configPath, {
52
- MY_SETTING: "from_file",
53
- LOG_LEVEL: "debug",
54
- });
55
- const config = new TestConfig(configPath);
56
- await config.reload();
57
- expect(config.MY_SETTING).toBe("from_file");
58
- expect(config.LOG_LEVEL).toBe("debug");
59
- });
60
-
61
- it("should prefer environment variables over config.json", async () => {
62
- fse.writeJsonSync(configPath, {
63
- MY_SETTING: "from_file",
64
- });
65
- process.env.MY_SETTING = "from_env";
66
- const config = new TestConfig(configPath);
67
- await config.reload();
68
- expect(config.MY_SETTING).toBe("from_env");
69
- });
70
-
71
- it("should mask sensitive fields in log output", async () => {
72
- const logs: string[] = [];
73
- const config = new TestConfig(configPath);
74
- await config.reload((msg) => logs.push(msg));
75
- const secretLog = logs.find((l) => l.includes("SECRET_KEY"));
76
- expect(secretLog).toContain("********************");
77
- expect(secretLog).not.toContain("secret_default");
78
- });
79
-
80
- it("should not mask non-sensitive fields in log output", async () => {
81
- const logs: string[] = [];
82
- const config = new TestConfig(configPath);
83
- await config.reload((msg) => logs.push(msg));
84
- const settingLog = logs.find((l) => l.includes("MY_SETTING"));
85
- expect(settingLog).toContain("default_value");
86
- });
87
-
88
- it("should survive missing config.json gracefully", async () => {
89
- const config = new TestConfig("/nonexistent/path/config.json");
90
- await config.reload();
91
- // Should keep defaults
92
- expect(config.MY_SETTING).toBe("default_value");
93
- });
94
-
95
- it("should register additional fields via addConfigField", async () => {
96
- fse.writeJsonSync(configPath, { MY_SETTING: "updated" });
97
- const config = new TestConfig(configPath);
98
- await config.reload();
99
- expect(config.MY_SETTING).toBe("updated");
100
- });
101
-
102
- it("should handle DATABASE_TYPE field", async () => {
103
- fse.writeJsonSync(configPath, { DATABASE_TYPE: "postgres" });
104
- const config = new TestConfig(configPath);
105
- await config.reload();
106
- expect(config.DATABASE_TYPE).toBe("postgres");
107
- });
108
- });