@onlineapps/conn-orch-validator 12.2.0 → 13.0.0

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 (56) hide show
  1. package/CHANGELOG.md +597 -0
  2. package/README.md +126 -19
  3. package/manifests/biz-service.manifest.json +15 -2
  4. package/manifests/library.manifest.json +4 -4
  5. package/package.json +11 -3
  6. package/src/CookbookTestRunner.js +275 -105
  7. package/src/CookbookTestUtils.js +79 -68
  8. package/src/ServiceReadinessValidator.js +42 -52
  9. package/src/ValidationOrchestrator.js +65 -44
  10. package/src/cli/biz-ci-gate.js +2 -2
  11. package/src/cli/oa-sync-template.js +97 -47
  12. package/src/cli/oa-validate.js +44 -10
  13. package/src/helpers/README.md +6 -6
  14. package/src/helpers/createServiceReadinessTests.js +87 -33
  15. package/src/index.js +14 -5
  16. package/src/lint/scripts/lintScripts.js +11 -4
  17. package/src/manifest/checks/libraryContext.js +6 -3
  18. package/src/manifest/checks/libraryDocs.js +174 -4
  19. package/src/manifest/checks/libraryTests.js +200 -19
  20. package/src/manifest/checks/scriptHeaders.js +6 -13
  21. package/src/manifest/checks/serviceConfig.js +36 -16
  22. package/src/manifest/checks/serviceConnectors.js +180 -2
  23. package/src/manifest/checks/serviceDb.js +0 -3
  24. package/src/manifest/checks/serviceScripts.js +3 -20
  25. package/src/manifest/runManifest.js +90 -13
  26. package/src/manifest/workspaceRoot.js +133 -4
  27. package/src/mocks/MockMQClient.js +2 -2
  28. package/src/sync/docsRegion.js +2 -2
  29. package/src/sync/readmeFile.js +30 -0
  30. package/src/sync/readmeLocation.js +2 -12
  31. package/src/sync/readmePointer.js +10 -4
  32. package/src/sync/serviceTemplate.js +9 -11
  33. package/src/sync/sharedEnv.js +59 -3
  34. package/src/sync/uniformFiles.js +81 -8
  35. package/src/utils/bizCiGateContract.js +2 -2
  36. package/src/utils/connectorContract.js +54 -2
  37. package/src/utils/cookbookFormat.js +25 -115
  38. package/src/utils/dbAccountGrants.js +5 -3
  39. package/src/utils/deployContract.js +153 -28
  40. package/src/utils/envContract.js +2 -2
  41. package/src/utils/handlerRef.js +8 -10
  42. package/src/utils/integrationRun.js +1 -1
  43. package/src/utils/operationsDocumentRules.js +242 -0
  44. package/src/utils/operationsRules.js +157 -0
  45. package/src/utils/resolveHeaders.js +12 -1
  46. package/src/utils/setupDatabase.js +1 -1
  47. package/src/utils/stepFailure.js +3 -3
  48. package/src/utils/stepReferences.js +28 -87
  49. package/src/utils/throwawaySchema.js +1 -1
  50. package/src/utils/yamlTopLevel.js +105 -0
  51. package/src/validators/ServiceStructureValidator.js +67 -152
  52. package/templates/business-service/README.md +3 -2
  53. package/templates/business-service/config/env-templates/shared.env +1 -0
  54. package/templates/business-service/src/config/index.js +15 -0
  55. package/TESTING_STRATEGY.md +0 -92
  56. package/jest.config.js +0 -37
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Which top-level key of a YAML document owns a line — the ONE definition of
5
+ * that question in this package.
6
+ *
7
+ * Two modules ask it, for two different purposes, and both used to answer it
8
+ * themselves: `src/sync/serviceTemplate.js` (which keys the platform block
9
+ * declares, and whose spans the sync replaces) and `src/utils/deployContract.js`
10
+ * (which JOB a `git reset --hard` sits in, so R2 can refuse a guard standing in
11
+ * another one). Two regexes for one fact, with two tolerances and no note that
12
+ * they differed: `.claude/rules/change-discipline.md` § One rail per concern.
13
+ *
14
+ * Column 0 is the whole rule, and it is enough for the files this reads: a
15
+ * `.gitlab-ci.yml` job is a top-level key and everything it owns is indented
16
+ * under it. Text in, positions out — nothing is re-serialized, so a comment and
17
+ * a quoting style survive (`.claude/rules/automation-gates.md` §1 requirement 3).
18
+ *
19
+ * The two readings differ in ONE thing, and it is a named predicate over this
20
+ * one regex rather than a second regex:
21
+ *
22
+ * `topLevelKeys` every key, hidden ones included. A hidden key
23
+ * (`.oa-uniform:`) is a declaration GitLab never runs
24
+ * as a job, but it is still a key, and a reader
25
+ * asking "which key is this line under" has to place
26
+ * it somewhere truthful.
27
+ * `rewritableTopLevelKeys` hidden keys dropped. This is the reading a
28
+ * GENERATOR may act on: `replaceTopLevelKeys`
29
+ * DELETES the spans it is given, and a hidden key a
30
+ * service wrote itself is not the platform's to
31
+ * delete (tests/unit/manifestCiBlock.test.js, G-CI
32
+ * over a pipeline that predates the markers).
33
+ */
34
+
35
+ /**
36
+ * A top-level mapping key: a line that starts in column 0 and ends its key with
37
+ * a colon. The leading dot is optional, because a hidden key is a key.
38
+ *
39
+ * The key's characters are what excludes the lines that are not keys: a list
40
+ * item (`- if: …`), a comment (`# build:`), an indented key, and anything with a
41
+ * space in it cannot match, so no `.gitlab-ci.yml` construct is mistaken for one.
42
+ */
43
+ const TOP_LEVEL_KEY = /^(\.?[A-Za-z_][A-Za-z0-9_.-]*):/gm;
44
+
45
+ /** What `topLevelKeyAt` answers for text that precedes the first key. */
46
+ const NO_TOP_LEVEL_KEY = '(before the first key)';
47
+
48
+ /** True for a hidden key — GitLab runs nothing whose name starts with a dot. */
49
+ function isHiddenKey(name) {
50
+ return name.startsWith('.');
51
+ }
52
+
53
+ /**
54
+ * Every top-level key of the document, in file order.
55
+ *
56
+ * @param {string} text the document
57
+ * @returns {Array<{name: string, at: number, line: number}>} `at` is the offset
58
+ * of the key in `text`, `line` its 0-based index in `text.split('\n')`.
59
+ */
60
+ function topLevelKeys(text) {
61
+ if (typeof text !== 'string') {
62
+ throw new Error('[YamlTopLevel] Missing document text - Expected the YAML as a string. '
63
+ + 'Fix: pass the file contents, not a path and not an array of lines.');
64
+ }
65
+ const keys = [];
66
+ TOP_LEVEL_KEY.lastIndex = 0;
67
+ let scanned = 0;
68
+ let line = 0;
69
+ let hit;
70
+ while ((hit = TOP_LEVEL_KEY.exec(text)) !== null) {
71
+ for (let i = scanned; i < hit.index; i += 1) if (text[i] === '\n') line += 1;
72
+ scanned = hit.index;
73
+ keys.push({ name: hit[1], at: hit.index, line });
74
+ }
75
+ return keys;
76
+ }
77
+
78
+ /**
79
+ * The top-level keys a generator may rewrite: every key except the hidden ones.
80
+ *
81
+ * @param {string} text the document
82
+ * @returns {Array<{name: string, at: number, line: number}>}
83
+ */
84
+ function rewritableTopLevelKeys(text) {
85
+ return topLevelKeys(text).filter((key) => !isHiddenKey(key.name));
86
+ }
87
+
88
+ /**
89
+ * The key an offset sits under — for a command in a `.gitlab-ci.yml`, the job
90
+ * whose shell runs it.
91
+ *
92
+ * @param {Array<{name: string, at: number}>} keys from `topLevelKeys`
93
+ * @param {number} offset an offset into the same document
94
+ * @returns {string} the key's name, or `NO_TOP_LEVEL_KEY`
95
+ */
96
+ function topLevelKeyAt(keys, offset) {
97
+ let name = NO_TOP_LEVEL_KEY;
98
+ for (const key of keys) {
99
+ if (key.at > offset) break;
100
+ name = key.name;
101
+ }
102
+ return name;
103
+ }
104
+
105
+ module.exports = { topLevelKeys, rewritableTopLevelKeys, topLevelKeyAt, NO_TOP_LEVEL_KEY, isHiddenKey };
@@ -17,7 +17,8 @@
17
17
 
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
- const { HANDLER_REF_PATTERN } = require('../utils/handlerRef');
20
+ const { operationsRuleRecords } = require('../utils/operationsRules');
21
+ const { operationsDocumentFindings, documentFindingRecord } = require('../utils/operationsDocumentRules');
21
22
 
22
23
  /**
23
24
  * Read a configuration file from the ONE place a service may keep it:
@@ -130,7 +131,7 @@ function readFile(root, rel) {
130
131
  * The body is `[^{}]*?`, NOT `[\s\S]*?`: a lazy any-character body starts at the
131
132
  * nearest preceding `const {` and swallows whole lines to reach the right
132
133
  * `require`, which dropped the first imported name. Measured on
133
- * `api_biz/meta/src/handlers/persons.js:10-11`, where an unrelated `models`
134
+ * the imports of `api_biz/meta/src/handlers/persons.js`, where an unrelated `models`
134
135
  * destructuring sits directly above the service-common one.
135
136
  *
136
137
  * @returns {string[]}
@@ -279,7 +280,7 @@ const STANDARD_LEVELS = [
279
280
  // infrastructureGate, health, validation, mq, registry, monitoring, state,
280
281
  // secrets and heartbeat — not tenantContext; `createTenantContextMiddleware`
281
282
  // was deleted on 2026-09-05 together with the runtime-defaults key
282
- // (docs/governance/confirmations/wrapper-tenant-middleware.md). A
283
+ // (api/docs/governance/confirmations/wrapper-tenant-middleware.md). A
283
284
  // level enforcing a dead declaration is the false guarantee automation-gates.md
284
285
  // §5 names, so the level is GONE rather than emptied — an empty level would
285
286
  // pass for every service while asserting nothing.
@@ -423,7 +424,7 @@ class ServiceStructureValidator {
423
424
  ? `Standard ${nextLevel.level} (${nextLevel.name}): ${check.message}`
424
425
  : `Standard ${nextLevel.level} (${nextLevel.name}): missing ${check.message}`,
425
426
  fix: check.fix
426
- || `Implement ${check.message} to reach standard ${nextLevel.level}. See docs/biz/60-templates/service-template.md`
427
+ || `Implement ${check.message} to reach standard ${nextLevel.level}. See api/docs/biz/60-templates/service-template.md`
427
428
  });
428
429
  }
429
430
  }
@@ -510,7 +511,7 @@ class ServiceStructureValidator {
510
511
  type: 'MISSING_CONFIG',
511
512
  path: 'config/service/config.json',
512
513
  message: 'Service configuration missing: config/service/config.json',
513
- fix: 'Create config.json with service metadata. See: /docs/biz/60-templates/service-template.md'
514
+ fix: 'Create config.json with service metadata. See: api/docs/biz/60-templates/service-template.md'
514
515
  });
515
516
  } else {
516
517
  try {
@@ -533,7 +534,7 @@ class ServiceStructureValidator {
533
534
  type: 'MISSING_OPERATIONS',
534
535
  path: 'config/service/operations.json',
535
536
  message: 'Operations specification missing: config/service/operations.json',
536
- fix: 'Create operations.json. See: /docs/biz/30-operations/schema-v3.md'
537
+ fix: 'Create operations.json. See: api/docs/biz/30-operations/schema-v3.md'
537
538
  });
538
539
  } else {
539
540
  try {
@@ -552,40 +553,42 @@ class ServiceStructureValidator {
552
553
  }
553
554
 
554
555
  /**
555
- * Validate config.json structure
556
- * Note: service.version is now read from package.json (Single Source of Truth)
556
+ * What step 1 still says about `config.json`: nothing about WHICH KEYS it
557
+ * carries. That question has one owner, the manifest row `C-SERVICE`, and
558
+ * this step is the structural half — the file is there, it reads, it parses.
559
+ *
560
+ * Until d.726 a `requiredFields = ['service.name']` lived here, with a
561
+ * message and a fix of its own, beside a row that demands the same key and
562
+ * two more. The four questions the removal has to answer
563
+ * (`.claude/rules/change-discipline.md` § Removing something removes its
564
+ * declaration):
565
+ *
566
+ * how it came to be — step 1 predates the manifest. When it was written it
567
+ * WAS the only reader of `config.json`, so the key list had to live
568
+ * somewhere and this was the only somewhere there was.
569
+ *
570
+ * which part of the concept carried it — service shape, and that part still
571
+ * stands. What moved is where it is declared: confirmation
572
+ * `biz-service-manifest` §2 makes the manifest the place a rule of service
573
+ * shape is written, with an id, an owner, a severity, a fix and a doc
574
+ * pointer. d.465 already moved boot step 2 onto it.
575
+ *
576
+ * why there were two — nobody removed the first when the second arrived.
577
+ *
578
+ * is the replacement MORE conceptual — yes, and measurably so. `C-SERVICE`
579
+ * demands three keys, not one, and each because grep found the reader that
580
+ * fails without it (`manifest/checks/serviceConfig.js`,
581
+ * REQUIRED_SERVICE_KEYS). The list here had one key and no reader named.
582
+ *
583
+ * And the two rails did not merely overlap: measured d.726, step 1 failing on
584
+ * `name` FAIL-FASTED the orchestrator, so step 2 never ran and a missing
585
+ * `workspaceScoped` — which the wrapper refuses to boot without — was not
586
+ * reported at all until `name` was fixed. The narrower rail was hiding the
587
+ * conceptual one.
588
+ *
589
+ * Note: service.version is read from package.json (Single Source of Truth).
557
590
  */
558
591
  validateConfigStructure(config) {
559
- // Required fields (version comes from package.json, not config.json).
560
- //
561
- // `service.port` was required here until 2026-08-30. ADR 0005 removed the
562
- // HTTP surface from biz containers, so nothing listens and the number has
563
- // no consumer — requiring it forced every repo to keep a declaration that
564
- // means nothing, which is what `change-discipline.md` § "Removing something
565
- // removes its declaration" exists to prevent. Absence is now correct;
566
- // presence is reported below as a warning, never as a failure.
567
- const requiredFields = [
568
- 'service.name'
569
- ];
570
-
571
- for (const field of requiredFields) {
572
- const parts = field.split('.');
573
- let value = config;
574
- for (const part of parts) {
575
- value = value?.[part];
576
- }
577
-
578
- if (!value) {
579
- this.errors.push({
580
- type: 'MISSING_CONFIG_FIELD',
581
- path: 'config/service/config.json',
582
- field: field,
583
- message: `Required field missing in config.json: ${field}`,
584
- fix: `Add "${field}" to config.json`
585
- });
586
- }
587
- }
588
-
589
592
  // A port that is still declared is a stale declaration, not a failure. Say
590
593
  // so out loud: `automation-gates.md` §5 — a check that stays silent about a
591
594
  // thing it can see is a false guarantee, and the eight services measured in
@@ -638,118 +641,19 @@ class ServiceStructureValidator {
638
641
  /**
639
642
  * Validate operations.json structure (v3 — handler registry dispatch).
640
643
  *
644
+ * Nothing about `operations.json` is decided in this method. The rules about
645
+ * the DOCUMENT (`schema_version`, an operations map with nothing in it) are
646
+ * `src/utils/operationsDocumentRules.js`; the rules about each OPERATION are
647
+ * `src/utils/operationsRules.js`, which asks
648
+ * `@onlineapps/service-validator-core` — the mirror of what the Registry
649
+ * refuses a registration on. Both used to be restated here, and both said
650
+ * something different from what the other rails said (d.465, d.465b).
651
+ *
641
652
  * @see api/docs/biz/30-operations/schema-v3.md § File shape
642
653
  */
643
654
  validateOperationsStructure(operations) {
644
- if (!operations.operations) {
645
- this.errors.push({
646
- type: 'INVALID_OPERATIONS_STRUCTURE',
647
- path: 'config/service/operations.json',
648
- message: 'operations.json must have "operations" key',
649
- fix: 'Wrap operations in {"operations": {...}} in config/service/operations.json'
650
- });
651
- return;
652
- }
653
-
654
- const ops = operations.operations;
655
- if (typeof ops !== 'object' || Array.isArray(ops)) {
656
- this.errors.push({
657
- type: 'INVALID_OPERATIONS_TYPE',
658
- path: 'config/service/operations.json',
659
- message: 'operations must be an object',
660
- fix: 'operations should be key-value pairs: {"operation-name": {...}}'
661
- });
662
- return;
663
- }
664
-
665
- if (operations.schema_version && operations.schema_version !== '3.0') {
666
- this.warnings.push({
667
- type: 'SCHEMA_VERSION_MISMATCH',
668
- path: 'config/service/operations.json',
669
- field: 'schema_version',
670
- value: operations.schema_version,
671
- message: `operations.json schema_version is "${operations.schema_version}" — expected "3.0"`,
672
- fix: 'Set schema_version to "3.0" in config/service/operations.json'
673
- });
674
- }
675
-
676
- if (Object.keys(ops).length === 0) {
677
- this.warnings.push({
678
- type: 'NO_OPERATIONS',
679
- path: 'config/service/operations.json',
680
- message: 'No operations defined',
681
- fix: 'Add at least one operation to operations.json'
682
- });
683
- return;
684
- }
685
-
686
- for (const [operationName, operationSpec] of Object.entries(ops)) {
687
- this.validateOperation(operationName, operationSpec);
688
- }
689
- }
690
-
691
- /**
692
- * Validate single operation structure (v3).
693
- * Required: handler, bundle_scope. Forbidden (v2): endpoint, method, path.
694
- *
695
- * @see api/docs/biz/30-operations/schema-v3.md § Per-operation keys
696
- */
697
- validateOperation(name, spec) {
698
- const requiredFields = ['handler', 'bundle_scope'];
699
-
700
- for (const field of requiredFields) {
701
- if (!spec[field]) {
702
- this.errors.push({
703
- type: 'MISSING_OPERATION_FIELD',
704
- path: 'config/service/operations.json',
705
- operation: name,
706
- field,
707
- message: `Operation "${name}" missing required field: ${field}`,
708
- fix: `Add "${field}" to operation "${name}" in config/service/operations.json`
709
- });
710
- }
711
- }
712
-
713
- if (spec.handler && !HANDLER_REF_PATTERN.test(spec.handler)) {
714
- this.errors.push({
715
- type: 'INVALID_HANDLER_REF',
716
- path: 'config/service/operations.json',
717
- operation: name,
718
- field: 'handler',
719
- value: spec.handler,
720
- message: `Handler must be in form 'handlers/<path>#<exportName>': ${spec.handler}`,
721
- fix: `Change handler to form 'handlers/v3/<file>#<exportName>'`
722
- });
723
- }
724
-
725
- const validScopes = ['platform', 'tenant', 'workspace'];
726
- if (spec.bundle_scope && !validScopes.includes(spec.bundle_scope)) {
727
- this.errors.push({
728
- type: 'INVALID_BUNDLE_SCOPE',
729
- path: 'config/service/operations.json',
730
- operation: name,
731
- field: 'bundle_scope',
732
- value: spec.bundle_scope,
733
- message: `Invalid bundle_scope: ${spec.bundle_scope}`,
734
- fix: `Use one of: ${validScopes.join(', ')}`
735
- });
736
- }
737
-
738
- // Reject v2 HTTP-routing fields (clean break per ARCHITECTURE_PRINCIPLES.md §11).
739
- const forbiddenV2Fields = ['endpoint', 'method', 'path'];
740
- for (const forbidden of forbiddenV2Fields) {
741
- if (forbidden in spec) {
742
- this.errors.push({
743
- type: 'V2_FIELD_PRESENT',
744
- path: 'config/service/operations.json',
745
- operation: name,
746
- field: forbidden,
747
- value: spec[forbidden],
748
- message: `Operation "${name}" has retired v2 field "${forbidden}" — not allowed in v3 schema`,
749
- fix: `Remove "${forbidden}" from operation "${name}" in config/service/operations.json — v3 dispatches via the handler registry, not by URL`
750
- });
751
- }
752
- }
655
+ this.errors.push(...operationsDocumentFindings(operations).map(documentFindingRecord));
656
+ this.errors.push(...operationsRuleRecords(operations && operations.operations).records);
753
657
  }
754
658
 
755
659
  /**
@@ -791,11 +695,22 @@ class ServiceStructureValidator {
791
695
  }
792
696
  }
793
697
 
794
- // Check for @onlineapps dependencies
698
+ // The @onlineapps packages a biz service cannot be without: the wrapper it
699
+ // boots through, and this validator, which its own gate runs.
700
+ //
701
+ // `@onlineapps/service-common` was a third entry and is not one any more.
702
+ // The v1.2 block at the top of this file records the decision it
703
+ // contradicted: `dep_service_common` and `business_error_usage` were
704
+ // RETIRED because the error hierarchy moved to the wrapper — so this file
705
+ // refused an import from that package up there and advised installing it
706
+ // down here. It stays a live package, which makes it a dependency a
707
+ // service adds when it needs what that package exports, never a
708
+ // recommendation for every service. See api/docs/biz/RETIRED-VOCABULARY.md,
709
+ // and tests/unit/serviceStructureRecommendedDeps.test.js for what was
710
+ // measured.
795
711
  const requiredDeps = [
796
712
  '@onlineapps/service-wrapper',
797
- '@onlineapps/conn-orch-validator',
798
- '@onlineapps/service-common'
713
+ '@onlineapps/conn-orch-validator'
799
714
  ];
800
715
 
801
716
  for (const dep of requiredDeps) {
@@ -844,7 +759,7 @@ class ServiceStructureValidator {
844
759
  type: 'MISSING_HANDLERS',
845
760
  path: 'src/handlers',
846
761
  message: 'Handlers directory missing: src/handlers/',
847
- fix: 'Create src/handlers/ with at least one v3 handler module. See: /docs/biz/60-templates/service-template.md'
762
+ fix: 'Create src/handlers/ with at least one v3 handler module. See: api/docs/biz/60-templates/service-template.md'
848
763
  });
849
764
  } else {
850
765
  this.info.push('✓ Found src/handlers/');
@@ -860,7 +775,7 @@ class ServiceStructureValidator {
860
775
  type: 'LEGACY_APP_JS',
861
776
  path: 'src/app.js',
862
777
  message: 'src/app.js is dead code post-ADR 0005 (bootstrap no longer reads it)',
863
- fix: 'Delete src/app.js; move any residual middleware into the wrapper adapter. See: /docs/biz/80-decisions/0005-no-http-in-biz-containers.md'
778
+ fix: 'Delete src/app.js; move any residual middleware into the wrapper adapter. See: api/docs/biz/80-decisions/0005-no-http-in-biz-containers.md'
864
779
  });
865
780
  }
866
781
 
@@ -8,7 +8,7 @@ Duty sections that apply:
8
8
  - `files`: F-INIT, F-JEST, F-RUNNER, F-GITIGNORE, F-DOCKERIGNORE, G-PROD, G-CI, G-PROD-IMAGE, G-README, G-SETUP, G-SETUP-INSTALL, G-SETUP-MATRIX, G-SETUP-VALIDATION, X-IGNORED, X-HOOKS, X-PREVAL, X-DB-CONFIG
9
9
  - `scripts`: S-TEST, S-ALL-C, S-INT-C, S-UNIT-C, S-COOKBOOKS, S-COOK-C, S-HOST, S-HOOKS
10
10
  - `tooling`: S-SCRIPTS
11
- - `config`: C-IDENTITY, C-SERVICE, C-OPS, C-CONTRACT, C-CONNECTORS, C-SERVICE-DEAD, C-ENV, G-SHARED-ENV, C-ENV-READS
11
+ - `config`: C-IDENTITY, C-SERVICE, C-OPS, C-CONTRACT, C-CONNECTORS, C-CI-CONNECTOR-ENV, C-SERVICE-DEAD, C-ENV, G-SHARED-ENV, C-ENV-READS
12
12
  - `runtime`: R-NODE, R-MEM, R-PID1, R-USER, R-PORTS-DEV, R-PORTS
13
13
  - `db`: D-DB-CONSISTENT, D-DB-COLLATION, D-DB-PACKAGE, D-DB-NAMING, D-DB-README, D-DB-ACCOUNT, D-DB-CI-ACCOUNT, D-DB-HEADERS
14
14
  - `docs`: C-LINT, D-PORT, D-NPM, D-SCRIPT, D-RETIRED, D-HEADER, D-LINT
@@ -17,7 +17,7 @@ Paths a duty owns:
17
17
 
18
18
  - `.dockerignore`: F-DOCKERIGNORE (from `./node_modules/@onlineapps/conn-orch-validator/templates/business-service/gitignore`)
19
19
  - `.gitignore`: F-GITIGNORE (from `./node_modules/@onlineapps/conn-orch-validator/templates/business-service/gitignore`)
20
- - `.gitlab-ci.yml`: G-CI (from `./node_modules/@onlineapps/conn-orch-validator/templates/business-service/.gitlab-ci.yml`), D-DB-CI-ACCOUNT
20
+ - `.gitlab-ci.yml`: G-CI (from `./node_modules/@onlineapps/conn-orch-validator/templates/business-service/.gitlab-ci.yml`), C-CI-CONNECTOR-ENV, D-DB-CI-ACCOUNT
21
21
  - `README.md`: G-README (from `./node_modules/@onlineapps/conn-orch-validator/manifests/biz-service.manifest.json`)
22
22
  - `config/biz-docs-lint.tree.json`: C-LINT
23
23
  - `config/env-templates`: C-ENV, D-DB-ACCOUNT
@@ -84,6 +84,7 @@ config/
84
84
  ├── env-templates/ # Versioned env templates
85
85
  └── env-active/ # Runtime env (gitignored)
86
86
  src/
87
+ ├── config/index.js # ConfigLoader.loadAll — the one place bootstrap() reads the configuration
87
88
  └── handlers/v3/ # One module per operation; the handler: field points here
88
89
  tests/
89
90
  ├── unit/
@@ -2,6 +2,7 @@
2
2
  # api/config/shared-env.json, then run `npx oa-sync-template shared-env --target <dir>`.
3
3
 
4
4
  # The runtime environment name every platform library resolves through its own runtime-config schema; without it a library cannot say which environment it is reporting from.
5
+ # @per-machine — the environment a process runs in is a property of the box, not of the platform: development on a developer machine, production on a deployed one, so the template value is the dev value and a deployed box differs from it on purpose
5
6
  NODE_ENV=development
6
7
 
7
8
  # The log level an infrastructure service's config/logging.json resolves through ${LOG_LEVEL}; one platform value so a stack does not log at a different level in every service. In the business chain the key is carried, not read: no library in the business chain reads it - @onlineapps/monitoring-core reads no environment variable at all (principle 1) and takes its level from the config the service hands it - so setting it on a business service changes nothing. It reaches every bearer because the shared key set is ONE file whose every copy is byte-identical to the platform template (confirmation biz-service-manifest 003 §18), never because each bearer reads every key.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Configuration loader
3
+ * Centralized configuration management
4
+ *
5
+ * Uses ConfigLoader utility for consistent, traceable config loading
6
+ */
7
+
8
+ const path = require('path');
9
+ const { ConfigLoader } = require('@onlineapps/service-wrapper');
10
+
11
+ // Load all configurations using unified loader (explicit basePath + env)
12
+ const basePath = path.join(__dirname, '..', '..');
13
+ const config = ConfigLoader.loadAll({ basePath, env: process.env });
14
+
15
+ module.exports = config;
@@ -1,92 +0,0 @@
1
- # Integration Testing Strategy Analysis
2
-
3
- ## 📋 Testing Scope Analysis
4
-
5
- ### ✅ DÁVÁ SMYSL - Rozsáhlé E2E testy
6
- 1. **ServiceWrapper E2E testy**
7
- - Kompletní flow: MQ → Wrapper → HTTP → Response
8
- - Multi-step workflows
9
- - Error handling & retry logic
10
- - Circuit breaker testing
11
- - Performance & load testing
12
-
13
- 2. **conn-orch-validator package**
14
- - Orchestruje všechny E2E testy
15
- - Testuje kompletní workflow scenarios
16
- - Simuluje reálné use cases
17
-
18
- 3. **Infrastrukturní uzly**
19
- - RabbitMQ integration (message flow, dead letter queues)
20
- - Redis integration (caching, session management)
21
- - Registry integration (service discovery, health checks)
22
-
23
- ### ❓ ZVÁŽIT - Unit testy jednotlivých balíčků
24
- - Jednotlivé connectors už mají unit testy ✅
25
- - Integration testy jednotlivých connectors možná redundantní
26
- - Lepší je testovat je jako součást větších E2E testů
27
-
28
- ## 🎯 Priority Testing Areas
29
-
30
- ### 1. Critical Path Testing
31
- - **hello-service complete flow** ✅ DONE
32
- - HTTP server running
33
- - MQ wrapper listening
34
- - Message processing working
35
-
36
- ### 2. ServiceWrapper Integration
37
- - Registry registration/heartbeat
38
- - MQ message consumption
39
- - HTTP service invocation
40
- - Response handling
41
- - Error scenarios
42
-
43
- ### 3. Workflow Orchestration
44
- - Multi-step workflows
45
- - Conditional branching
46
- - Parallel execution
47
- - Error recovery
48
-
49
- ### 4. Infrastructure Resilience
50
- - Service failures & recovery
51
- - Network partitions
52
- - Message queue failures
53
- - Cache misses
54
-
55
- ## 📝 Implementation Progress
56
-
57
- ### Completed
58
- - ✅ hello-service hybrid architecture implemented
59
- - ✅ MQ wrapper successfully starts and listens
60
- - ✅ Fixed all initialization issues:
61
- - ServiceWrapper consume() parameter order
62
- - RegistryClient queueManager.init()
63
- - Redis cache singleton issues
64
- - LoggerConnector initialization in ServiceWrapper
65
- - ✅ ServiceWrapper E2E test suite created
66
- - ✅ Test infrastructure setup (Jest, Express for mock services)
67
- - ✅ Fixed message parsing from AMQP buffer to JSON
68
- - ✅ Created test-mq-flow.js for testing message flow (script removed 2026-08-26 — no consumer)
69
- - ✅ Fixed cookbook validation (added type field to steps)
70
-
71
- ### In Progress
72
- - 🔧 Verifying complete E2E message flow
73
- - 🔧 Testing workflow orchestration with real services
74
-
75
- ### TODO
76
- - [ ] Implement response queue listener for capturing results
77
- - [ ] Complete E2E test suite with all scenarios
78
- - [ ] Load testing scenarios
79
- - [ ] Chaos engineering tests
80
- - [ ] Performance benchmarks
81
-
82
- ## 🚀 Next Session Actions
83
-
84
- 1. **Complete conn-orch-validator package**
85
- 2. **Run full E2E test suite**
86
- 3. **Document test results**
87
- 4. **Fix any issues found**
88
- 5. **Performance optimization based on test results**
89
-
90
- ---
91
- *Last updated: 2025-09-26*
92
- *Session ending at 5hr limit*
package/jest.config.js DELETED
@@ -1,37 +0,0 @@
1
- 'use strict';
2
-
3
- process.env.TESTING_TENANT_ID = process.env.TESTING_TENANT_ID || '99';
4
- process.env.TESTING_WORKSPACE_ID = process.env.TESTING_WORKSPACE_ID || '200';
5
-
6
- module.exports = {
7
- testEnvironment: 'node',
8
- coverageDirectory: 'coverage',
9
- collectCoverageFrom: [
10
- 'src/**/*.js',
11
- '!src/index.js'
12
- ],
13
- testMatch: [
14
- '**/tests/**/*.test.js'
15
- ],
16
-
17
- // tests/fixtures/manifest/** holds SIMULATED CHECKOUTS that the manifest
18
- // suites read from disk as data, never require. Jest's haste map indexes every
19
- // package.json it crawls, so the three fixture workspaces that each carry the
20
- // service `biz-alpha` read as three modules of one name and the crawl prints
21
- // "Haste module naming collision" on stdout. The reason the fixtures keep
22
- // their names, and why the pattern stops at `manifest/` rather than covering
23
- // tests/fixtures/ whole, is written once — in the repository's jest.config.js,
24
- // beside the same string. This config owns the same exclusion for its own map,
25
- // because rootDir here is the package.
26
- modulePathIgnorePatterns: ['/tests/fixtures/manifest/'],
27
- coverageThreshold: {
28
- global: {
29
- branches: 80,
30
- functions: 80,
31
- lines: 80,
32
- statements: 80
33
- }
34
- },
35
- verbose: true,
36
- testTimeout: 10000
37
- };