@resolveio/server-lib 22.3.73 → 22.3.75
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.
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildResolveIORunnerQaEnvScript = buildResolveIORunnerQaEnvScript;
|
|
4
4
|
exports.buildResolveIORunnerLocalQaScript = buildResolveIORunnerLocalQaScript;
|
|
5
5
|
exports.buildResolveIORunnerLocalQaStopperScript = buildResolveIORunnerLocalQaStopperScript;
|
|
6
|
+
exports.buildResolveIORunnerQaLiveDataSeederScript = buildResolveIORunnerQaLiveDataSeederScript;
|
|
6
7
|
exports.buildResolveIORunnerBugfixComparisonQaScript = buildResolveIORunnerBugfixComparisonQaScript;
|
|
7
8
|
exports.buildResolveIORunnerQaToolsReadme = buildResolveIORunnerQaToolsReadme;
|
|
8
9
|
var runner_process_janitor_1 = require("./runner-process-janitor");
|
|
@@ -53,6 +54,10 @@ function buildResolveIORunnerQaEnvScript(options) {
|
|
|
53
54
|
var altReuseVar = envVar(altMode, 'REUSE_RUNNING');
|
|
54
55
|
var keepaliveVar = envVar(mode, 'KEEPALIVE');
|
|
55
56
|
var altKeepaliveVar = envVar(altMode, 'KEEPALIVE');
|
|
57
|
+
var mongoPortVar = envVar(mode, 'MONGO_PORT');
|
|
58
|
+
var altMongoPortVar = envVar(altMode, 'MONGO_PORT');
|
|
59
|
+
var mongoUrlVar = envVar(mode, 'MONGO_URL');
|
|
60
|
+
var altMongoUrlVar = envVar(altMode, 'MONGO_URL');
|
|
56
61
|
var browserLoopVar = mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_BROWSER' : 'RESOLVEIO_RUNNER_QA_BROWSER';
|
|
57
62
|
return [
|
|
58
63
|
"export ".concat(mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP', "=\"").concat('${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP') + ':-' + tmpRoot + '}', "\""),
|
|
@@ -102,6 +107,11 @@ function buildResolveIORunnerQaEnvScript(options) {
|
|
|
102
107
|
"export ".concat(altReuseVar, "=\"").concat('${' + altReuseVar + ':-${' + reuseVar + '}}', "\""),
|
|
103
108
|
"export ".concat(keepaliveVar, "=\"").concat('${' + keepaliveVar + ':-${' + altKeepaliveVar + ':-false}}', "\""),
|
|
104
109
|
"export ".concat(altKeepaliveVar, "=\"").concat('${' + altKeepaliveVar + ':-${' + keepaliveVar + '}}', "\""),
|
|
110
|
+
"export ".concat(mongoPortVar, "=\"").concat('${' + mongoPortVar + ':-${' + altMongoPortVar + ':-3001}}', "\""),
|
|
111
|
+
"export ".concat(altMongoPortVar, "=\"").concat('${' + altMongoPortVar + ':-${' + mongoPortVar + '}}', "\""),
|
|
112
|
+
"export ".concat(mongoUrlVar, "=\"").concat('${' + mongoUrlVar + ':-${' + altMongoUrlVar + ':-mongodb://127.0.0.1:${' + mongoPortVar + '}/resolveio?directConnection=true}}', "\""),
|
|
113
|
+
"export ".concat(altMongoUrlVar, "=\"").concat('${' + altMongoUrlVar + ':-${' + mongoUrlVar + '}}', "\""),
|
|
114
|
+
"export MONGO_URL=\"".concat('${MONGO_URL:-$' + mongoUrlVar + '}', "\""),
|
|
105
115
|
"export ".concat(usernameVar, "=\"").concat('${' + usernameVar + ':-${' + altUsernameVar + ':-' + username + '}}', "\""),
|
|
106
116
|
"export ".concat(altUsernameVar, "=\"").concat('${' + altUsernameVar + ':-${' + usernameVar + '}}', "\""),
|
|
107
117
|
"export ".concat(passwordVar, "=\"").concat('${' + passwordVar + ':-${' + altPasswordVar + ':-' + password + '}}', "\""),
|
|
@@ -641,6 +651,263 @@ function buildResolveIORunnerLocalQaStopperScript() {
|
|
|
641
651
|
''
|
|
642
652
|
].join('\n');
|
|
643
653
|
}
|
|
654
|
+
function buildResolveIORunnerQaLiveDataSeederScript() {
|
|
655
|
+
return [
|
|
656
|
+
'#!/usr/bin/env node',
|
|
657
|
+
'const fs = require("fs");',
|
|
658
|
+
'const path = require("path");',
|
|
659
|
+
'',
|
|
660
|
+
'const projectRoot = path.resolve(process.argv[2] || process.cwd());',
|
|
661
|
+
'const repoRoot = findRepoRoot(projectRoot);',
|
|
662
|
+
'const artifactDir = path.join(projectRoot, "qa-artifacts");',
|
|
663
|
+
'fs.mkdirSync(artifactDir, { recursive: true });',
|
|
664
|
+
'const resultPath = path.join(artifactDir, "qa-live-data-seed-result.json");',
|
|
665
|
+
'',
|
|
666
|
+
'function writeResult(payload, exitCode = 0) {',
|
|
667
|
+
' const full = { ...payload, created_at: new Date().toISOString() };',
|
|
668
|
+
' fs.writeFileSync(resultPath, JSON.stringify(full, null, 2));',
|
|
669
|
+
' console.log(JSON.stringify(full, null, 2));',
|
|
670
|
+
' process.exit(exitCode);',
|
|
671
|
+
'}',
|
|
672
|
+
'',
|
|
673
|
+
'function findRepoRoot(start) {',
|
|
674
|
+
' let current = start;',
|
|
675
|
+
' for (let i = 0; i < 8; i += 1) {',
|
|
676
|
+
' if (fs.existsSync(path.join(current, ".git"))) return current;',
|
|
677
|
+
' const parent = path.dirname(current);',
|
|
678
|
+
' if (parent === current) break;',
|
|
679
|
+
' current = parent;',
|
|
680
|
+
' }',
|
|
681
|
+
' return start;',
|
|
682
|
+
'}',
|
|
683
|
+
'',
|
|
684
|
+
'function readJsonIfExists(filePath) {',
|
|
685
|
+
' try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (error) { return null; }',
|
|
686
|
+
'}',
|
|
687
|
+
'',
|
|
688
|
+
'function resolveRuntimeSource() {',
|
|
689
|
+
' const envUri = process.env.RESOLVEIO_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_URL || "";',
|
|
690
|
+
' const envDb = process.env.RESOLVEIO_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_DB || "";',
|
|
691
|
+
' if (envUri) return { uri: envUri, database: envDb };',
|
|
692
|
+
' const candidates = [',
|
|
693
|
+
' path.join(repoRoot, ".resolveio-support-context", "mongo-context", ".mongo-runtime.json"),',
|
|
694
|
+
' path.join(projectRoot, ".resolveio-support-context", "mongo-context", ".mongo-runtime.json"),',
|
|
695
|
+
' path.join(repoRoot, ".resolveio-context", "mongo-context", ".mongo-runtime.json"),',
|
|
696
|
+
' path.join(projectRoot, ".resolveio-context", "mongo-context", ".mongo-runtime.json")',
|
|
697
|
+
' ];',
|
|
698
|
+
' for (const candidate of candidates) {',
|
|
699
|
+
' const parsed = readJsonIfExists(candidate);',
|
|
700
|
+
' if (parsed && parsed.mongo_uri) return { uri: String(parsed.mongo_uri || ""), database: String(parsed.mongo_db || "") };',
|
|
701
|
+
' }',
|
|
702
|
+
' return { uri: "", database: "" };',
|
|
703
|
+
'}',
|
|
704
|
+
'',
|
|
705
|
+
'function redactUri(uri) {',
|
|
706
|
+
' return String(uri || "").replace(/(mongodb(?:\\+srv)?:\\/\\/)([^:@/?#]+):([^@/?#]+)@/i, "$1$2:***@");',
|
|
707
|
+
'}',
|
|
708
|
+
'',
|
|
709
|
+
'function isLocalMongoUri(uri) {',
|
|
710
|
+
' return /mongodb(?:\\+srv)?:\\/\\/(?:[^@/]+@)?(?:127\\.0\\.0\\.1|localhost)(?::\\d+)?\\//i.test(String(uri || ""));',
|
|
711
|
+
'}',
|
|
712
|
+
'',
|
|
713
|
+
'function requireMongo() {',
|
|
714
|
+
' const candidates = [',
|
|
715
|
+
' path.join(projectRoot, "server", "node_modules", "mongodb"),',
|
|
716
|
+
' path.join(projectRoot, "node_modules", "mongodb"),',
|
|
717
|
+
' path.join(repoRoot, "node_modules", "mongodb"),',
|
|
718
|
+
' "mongodb"',
|
|
719
|
+
' ];',
|
|
720
|
+
' const errors = [];',
|
|
721
|
+
' for (const candidate of candidates) {',
|
|
722
|
+
' try { return require(candidate); } catch (error) { errors.push(`${candidate}: ${error.message}`); }',
|
|
723
|
+
' }',
|
|
724
|
+
' throw new Error(`Unable to require mongodb package. ${errors.join(" | ")}`);',
|
|
725
|
+
'}',
|
|
726
|
+
'',
|
|
727
|
+
'function idValues(docs, keys) {',
|
|
728
|
+
' const values = new Set();',
|
|
729
|
+
' for (const doc of docs || []) {',
|
|
730
|
+
' for (const key of keys) {',
|
|
731
|
+
' const value = doc && doc[key];',
|
|
732
|
+
' if (typeof value === "string" && value.trim()) values.add(value.trim());',
|
|
733
|
+
' if (Array.isArray(value)) value.forEach((entry) => typeof entry === "string" && entry.trim() && values.add(entry.trim()));',
|
|
734
|
+
' }',
|
|
735
|
+
' }',
|
|
736
|
+
' return Array.from(values);',
|
|
737
|
+
'}',
|
|
738
|
+
'',
|
|
739
|
+
'function unique(values) { return Array.from(new Set((values || []).filter(Boolean).map(String))); }',
|
|
740
|
+
'',
|
|
741
|
+
'function readSeedHintText() {',
|
|
742
|
+
' const chunks = [process.env.RESOLVEIO_QA_SEED_HINTS || "", process.env.RESOLVEIO_SUPPORT_QA_SEED_HINTS || "", process.env.RESOLVEIO_RUNNER_QA_SEED_HINTS || ""];',
|
|
743
|
+
' const candidates = [',
|
|
744
|
+
' path.join(repoRoot, ".resolveio-support-context", "manual-ticket.request.txt"),',
|
|
745
|
+
' path.join(projectRoot, ".resolveio-support-context", "manual-ticket.request.txt"),',
|
|
746
|
+
' path.join(repoRoot, ".resolveio-context", "manual-ticket.request.txt"),',
|
|
747
|
+
' path.join(projectRoot, ".resolveio-context", "manual-ticket.request.txt")',
|
|
748
|
+
' ];',
|
|
749
|
+
' for (const candidate of candidates) {',
|
|
750
|
+
' try { chunks.push(fs.readFileSync(candidate, "utf8")); } catch (error) {}',
|
|
751
|
+
' }',
|
|
752
|
+
' return chunks.filter(Boolean).join("\\n");',
|
|
753
|
+
'}',
|
|
754
|
+
'',
|
|
755
|
+
'function extractSeedIdentifiers() {',
|
|
756
|
+
' const text = readSeedHintText();',
|
|
757
|
+
' const identifiers = new Set();',
|
|
758
|
+
' const explicit = /\\b(?:invoice|inv|bol|order|pso|ticket|wo|work\\s*order|delivery|treatment)\\s*(?:#|no\\.?|number|num)?\\s*[:#-]?\\s*([A-Z0-9][A-Z0-9._-]{2,})\\b/gi;',
|
|
759
|
+
' let match;',
|
|
760
|
+
' while ((match = explicit.exec(text)) !== null) identifiers.add(match[1]);',
|
|
761
|
+
' const numeric = /\\b\\d{4,}\\b/g;',
|
|
762
|
+
' while ((match = numeric.exec(text)) !== null) identifiers.add(match[0]);',
|
|
763
|
+
' return Array.from(identifiers)',
|
|
764
|
+
' .map((value) => String(value || "").trim())',
|
|
765
|
+
' .filter((value) => value && !/^00?4\\d{3}$/.test(value))',
|
|
766
|
+
' .slice(0, 20);',
|
|
767
|
+
'}',
|
|
768
|
+
'',
|
|
769
|
+
'function identifierQuery(identifiers) {',
|
|
770
|
+
' const ors = [];',
|
|
771
|
+
' const fields = ["_id", "invoice_number", "invoice_number_string", "order_number", "order_number_string", "activity_number", "bol_number", "bol_string", "ticket_number", "ticket", "wo_number", "work_order_number", "delivery_number", "treatment_number"];',
|
|
772
|
+
' for (const identifier of identifiers || []) {',
|
|
773
|
+
' const escaped = String(identifier).replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&");',
|
|
774
|
+
' const regex = new RegExp(escaped, "i");',
|
|
775
|
+
' for (const field of fields) ors.push({ [field]: regex });',
|
|
776
|
+
' ors.push({ "bol.bol_string": regex });',
|
|
777
|
+
' ors.push({ "bol.bol_number": regex });',
|
|
778
|
+
' }',
|
|
779
|
+
' return ors.length ? { $or: ors } : null;',
|
|
780
|
+
'}',
|
|
781
|
+
'',
|
|
782
|
+
'async function copyByIds(sourceDb, targetDb, collectionName, ids, summary, limit = 100) {',
|
|
783
|
+
' const cleanIds = unique(ids).slice(0, limit);',
|
|
784
|
+
' if (!cleanIds.length) return [];',
|
|
785
|
+
' const docs = await sourceDb.collection(collectionName).find({ _id: { $in: cleanIds } }).limit(limit).toArray();',
|
|
786
|
+
' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',
|
|
787
|
+
' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',
|
|
788
|
+
' return docs;',
|
|
789
|
+
'}',
|
|
790
|
+
'',
|
|
791
|
+
'async function copyQuery(sourceDb, targetDb, collectionName, query, summary, limit = 50, sort = null) {',
|
|
792
|
+
' let cursor = sourceDb.collection(collectionName).find(query || {});',
|
|
793
|
+
' if (sort) cursor = cursor.sort(sort);',
|
|
794
|
+
' const docs = await cursor.limit(limit).toArray();',
|
|
795
|
+
' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',
|
|
796
|
+
' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',
|
|
797
|
+
' return docs;',
|
|
798
|
+
'}',
|
|
799
|
+
'',
|
|
800
|
+
'async function seedBillingInventory(sourceDb, targetDb) {',
|
|
801
|
+
' const summary = { profile: "billing_inventory", collections: {}, selected: {}, notes: [] };',
|
|
802
|
+
' const identifiers = extractSeedIdentifiers();',
|
|
803
|
+
' summary.selected.seed_identifiers = identifiers;',
|
|
804
|
+
' const billableDeliveryQuery = { $and: [',
|
|
805
|
+
' { $or: [{ type: "Delivery" }, { type: "Pickup" }] },',
|
|
806
|
+
' { $or: [{ consignment: { $exists: false } }, { consignment: false }] },',
|
|
807
|
+
' { approved: true }, { invoiced: false }, { generic: false }, { "invoices.0": { $exists: false } },',
|
|
808
|
+
' { $or: [{ billed: true }, { memo_bill: true }] }',
|
|
809
|
+
' ] };',
|
|
810
|
+
' const billableTruckTreatQuery = { $and: [',
|
|
811
|
+
' { type: "Truck Treat" }, { invoiced: false }, { generic: false }, { approved: true }, { "invoices.0": { $exists: false } },',
|
|
812
|
+
' { $or: [{ billed: true }, { memo_bill: true }] }',
|
|
813
|
+
' ] };',
|
|
814
|
+
' const hintedQuery = identifierQuery(identifiers);',
|
|
815
|
+
' let hintedProductionDeliveries = [];',
|
|
816
|
+
' let hintedTruckTreatingDeliveries = [];',
|
|
817
|
+
' if (hintedQuery) {',
|
|
818
|
+
' hintedProductionDeliveries = await copyQuery(sourceDb, targetDb, "production-deliveries", { $and: [billableDeliveryQuery, hintedQuery] }, summary, 3, { date: -1, date_ship: -1 });',
|
|
819
|
+
' hintedTruckTreatingDeliveries = await copyQuery(sourceDb, targetDb, "truck-treating-deliveries", { $and: [billableTruckTreatQuery, hintedQuery] }, summary, 3, { date: -1, date_treated: -1 });',
|
|
820
|
+
' if (hintedProductionDeliveries.length || hintedTruckTreatingDeliveries.length) summary.notes.push(`Preferred live records matching ticket identifiers: ${identifiers.join(", ")}`);',
|
|
821
|
+
' else summary.notes.push(`No billable live records matched ticket identifiers: ${identifiers.join(", ")}`);',
|
|
822
|
+
' }',
|
|
823
|
+
'',
|
|
824
|
+
' const fallbackProductionDeliveries = hintedProductionDeliveries.length ? [] : await copyQuery(sourceDb, targetDb, "production-deliveries", billableDeliveryQuery, summary, 3, { date: -1, date_ship: -1 });',
|
|
825
|
+
' const fallbackTruckTreatingDeliveries = hintedTruckTreatingDeliveries.length ? [] : await copyQuery(sourceDb, targetDb, "truck-treating-deliveries", billableTruckTreatQuery, summary, 3, { date: -1, date_treated: -1 });',
|
|
826
|
+
' const productionDeliveries = [...hintedProductionDeliveries, ...fallbackProductionDeliveries];',
|
|
827
|
+
' const truckTreatingDeliveries = [...hintedTruckTreatingDeliveries, ...fallbackTruckTreatingDeliveries];',
|
|
828
|
+
' summary.selected.production_deliveries = productionDeliveries.map((doc) => doc._id);',
|
|
829
|
+
' summary.selected.truck_treating_deliveries = truckTreatingDeliveries.map((doc) => doc._id);',
|
|
830
|
+
' if (!productionDeliveries.length && !truckTreatingDeliveries.length) {',
|
|
831
|
+
' summary.notes.push("No live billable delivery or truck-treatment records matched the Billing Dashboard awaiting-invoice filters.");',
|
|
832
|
+
' }',
|
|
833
|
+
'',
|
|
834
|
+
' const sourceDocs = [...productionDeliveries, ...truckTreatingDeliveries];',
|
|
835
|
+
' const customerIds = idValues(sourceDocs, ["id_customer"]);',
|
|
836
|
+
' const locationIds = idValues(sourceDocs, ["id_location"]);',
|
|
837
|
+
' const wellGroupIds = idValues(sourceDocs, ["id_well_group"]);',
|
|
838
|
+
' const itemIds = idValues(sourceDocs, ["id_item", "id_chemical"]);',
|
|
839
|
+
' const bolIds = idValues(sourceDocs, ["id_bol", "id_bols"]);',
|
|
840
|
+
' const yardIds = idValues(sourceDocs, ["id_yard"]);',
|
|
841
|
+
' const userIds = idValues(sourceDocs, ["id_user_approved", "id_driver", "id_driver_scheduled", "id_account_manager", "id_foreman"]);',
|
|
842
|
+
'',
|
|
843
|
+
' const copiedLocations = await copyByIds(sourceDb, targetDb, "production-locations", locationIds, summary);',
|
|
844
|
+
' const copiedWellGroups = await copyByIds(sourceDb, targetDb, "well-groups", wellGroupIds, summary);',
|
|
845
|
+
' const copiedItems = await copyByIds(sourceDb, targetDb, "items", itemIds, summary);',
|
|
846
|
+
' const copiedChemicals = await copyByIds(sourceDb, targetDb, "chemicals", itemIds, summary);',
|
|
847
|
+
' await copyByIds(sourceDb, targetDb, "customers", unique([...customerIds, ...idValues(copiedLocations, ["id_customer"]), ...idValues(copiedWellGroups, ["id_customer"])]), summary);',
|
|
848
|
+
' await copyByIds(sourceDb, targetDb, "bols", bolIds, summary);',
|
|
849
|
+
' await copyByIds(sourceDb, targetDb, "yards", yardIds, summary);',
|
|
850
|
+
' await copyByIds(sourceDb, targetDb, "users", userIds, summary);',
|
|
851
|
+
'',
|
|
852
|
+
' const copiedServiceItems = await copyQuery(sourceDb, targetDb, "items", { $or: [',
|
|
853
|
+
' { type: /service/i }, { type: /misc/i }, { category: /service/i }, { category: /misc/i },',
|
|
854
|
+
' { name: /surcharge|fuel|delivery|service|misc/i }',
|
|
855
|
+
' ] }, summary, 40, { updatedAt: -1 });',
|
|
856
|
+
' summary.selected.service_or_surcharge_items = copiedServiceItems.map((doc) => doc._id);',
|
|
857
|
+
'',
|
|
858
|
+
' await copyQuery(sourceDb, targetDb, "sales-taxes", {}, summary, 80, { updatedAt: -1 });',
|
|
859
|
+
' await copyQuery(sourceDb, targetDb, "state-counties", {}, summary, 80, { updatedAt: -1 });',
|
|
860
|
+
' await copyQuery(sourceDb, targetDb, "accounting-codes", {}, summary, 80, { updatedAt: -1 });',
|
|
861
|
+
' await copyQuery(sourceDb, targetDb, "pricing-items", {}, summary, 40, { updatedAt: -1 });',
|
|
862
|
+
' await copyQuery(sourceDb, targetDb, "pricing-item-miscs", {}, summary, 40, { updatedAt: -1 });',
|
|
863
|
+
' await copyQuery(sourceDb, targetDb, "pricing-item-services", {}, summary, 40, { updatedAt: -1 });',
|
|
864
|
+
'',
|
|
865
|
+
' const inventoryLocationQuery = { $or: [',
|
|
866
|
+
' { id_item: { $in: unique([...itemIds, ...copiedItems.map((doc) => doc._id), ...copiedServiceItems.map((doc) => doc._id)]) } },',
|
|
867
|
+
' { id_chemical: { $in: unique([...itemIds, ...copiedChemicals.map((doc) => doc._id)]) } },',
|
|
868
|
+
' { id_yard: { $in: yardIds } }',
|
|
869
|
+
' ] };',
|
|
870
|
+
' const inventoryLocations = await copyQuery(sourceDb, targetDb, "inventory-locations", inventoryLocationQuery, summary, 80, { updatedAt: -1 });',
|
|
871
|
+
' await copyQuery(sourceDb, targetDb, "inventory-transactions", { $or: [',
|
|
872
|
+
' { id_inventory_location: { $in: inventoryLocations.map((doc) => doc._id) } },',
|
|
873
|
+
' { id_item: { $in: itemIds } },',
|
|
874
|
+
' { id_chemical: { $in: itemIds } }',
|
|
875
|
+
' ] }, summary, 120, { date: -1, updatedAt: -1 });',
|
|
876
|
+
'',
|
|
877
|
+
' summary.ready = productionDeliveries.length > 0 || truckTreatingDeliveries.length > 0;',
|
|
878
|
+
' return summary;',
|
|
879
|
+
'}',
|
|
880
|
+
'',
|
|
881
|
+
'(async () => {',
|
|
882
|
+
' const { MongoClient } = requireMongo();',
|
|
883
|
+
' const source = resolveRuntimeSource();',
|
|
884
|
+
' const targetUri = process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.MONGO_URL || "mongodb://127.0.0.1:3001/resolveio?directConnection=true";',
|
|
885
|
+
' if (!source.uri) writeResult({ status: "skipped", reason: "missing_live_mongo_uri", result_path: resultPath }, 0);',
|
|
886
|
+
' if (!isLocalMongoUri(targetUri)) writeResult({ status: "failed", reason: "target_mongo_must_be_localhost", target_uri_redacted: redactUri(targetUri), result_path: resultPath }, 3);',
|
|
887
|
+
' if (String(source.uri) === String(targetUri)) writeResult({ status: "failed", reason: "source_and_target_mongo_match", result_path: resultPath }, 3);',
|
|
888
|
+
' const sourceClient = new MongoClient(source.uri, { readPreference: "secondaryPreferred", serverSelectionTimeoutMS: 15000 });',
|
|
889
|
+
' const targetClient = new MongoClient(targetUri, { serverSelectionTimeoutMS: 15000 });',
|
|
890
|
+
' try {',
|
|
891
|
+
' await sourceClient.connect();',
|
|
892
|
+
' await targetClient.connect();',
|
|
893
|
+
' const sourceDb = source.database ? sourceClient.db(source.database) : sourceClient.db();',
|
|
894
|
+
' const targetDb = targetClient.db();',
|
|
895
|
+
' const summary = await seedBillingInventory(sourceDb, targetDb);',
|
|
896
|
+
' writeResult({',
|
|
897
|
+
' status: summary.ready ? "pass" : "needs-data",',
|
|
898
|
+
' source_uri_redacted: redactUri(source.uri),',
|
|
899
|
+
' target_uri_redacted: redactUri(targetUri),',
|
|
900
|
+
' ...summary,',
|
|
901
|
+
' result_path: resultPath',
|
|
902
|
+
' }, summary.ready ? 0 : 4);',
|
|
903
|
+
' } finally {',
|
|
904
|
+
' await sourceClient.close().catch(() => undefined);',
|
|
905
|
+
' await targetClient.close().catch(() => undefined);',
|
|
906
|
+
' }',
|
|
907
|
+
'})().catch((error) => writeResult({ status: "failed", reason: error && (error.stack || error.message) || String(error), result_path: resultPath }, 2));',
|
|
908
|
+
''
|
|
909
|
+
].join('\n');
|
|
910
|
+
}
|
|
644
911
|
function buildResolveIORunnerBugfixComparisonQaScript() {
|
|
645
912
|
return [
|
|
646
913
|
'#!/usr/bin/env bash',
|
|
@@ -796,6 +1063,7 @@ function buildResolveIORunnerQaToolsReadme(options) {
|
|
|
796
1063
|
'```bash',
|
|
797
1064
|
"source ".concat(toolsDir, "/env.sh"),
|
|
798
1065
|
"".concat(toolsDir, "/run-local-qa.sh <project-root>"),
|
|
1066
|
+
"node ".concat(toolsDir, "/qa-live-data-seed.js <project-root>"),
|
|
799
1067
|
"node ".concat(toolsDir, "/qa-auth-bootstrap.js <project-root> /target-route"),
|
|
800
1068
|
"".concat(toolsDir, "/bugfix-comparison-qa.sh <project-root> origin/master -- bash -lc '<same QA command>'"),
|
|
801
1069
|
'```',
|
|
@@ -807,6 +1075,7 @@ function buildResolveIORunnerQaToolsReadme(options) {
|
|
|
807
1075
|
'Do not run `npm run build-dev`, `ng build`, or another Angular compile while keepalive `ng serve` is running. If a full Angular build is required after browser QA, first run the staged `stop-local-qa.sh`, then build, then restart `run-local-qa.sh` for final browser proof.',
|
|
808
1076
|
'Use desktop screenshots at 1920x1080 by default unless the task is explicitly mobile/responsive. Every screenshot must have a customer-facing caption.',
|
|
809
1077
|
'For import/export/form-submit/data workflows, prove before/action/after with representative data and a concrete row/count/value assertion.',
|
|
1078
|
+
'For data-backed workflows, run `qa-live-data-seed.js` after the local app is ready and before browser clickthrough. It reads a bounded live-data slice from `RESOLVEIO_QA_LIVE_MONGO_URL` or staged mongo context, writes only to localhost QA Mongo, prefers ticket-mentioned invoice/BOL/order/ticket identifiers, and records `qa-artifacts/qa-live-data-seed-result.json`.',
|
|
810
1079
|
'For bug fixes, use `bugfix-comparison-qa.sh` so baseline/master runs the exact same repro before the candidate/PR run. A passing candidate is not enough unless the comparison result shows baseline failed or the report explicitly explains why the baseline failure could not be reproduced.',
|
|
811
1080
|
"Use `$".concat(usernameVar, "` and `$").concat(passwordVar, "` for the local fixture admin account unless ticket/app-specific credentials are provided."),
|
|
812
1081
|
'The env file reuses `/var/lib/resolveio/puppeteer`, npm cache, worker-safe Browserslist settings, and Angular cache prep so QA should not download a browser or rebuild cold caches unnecessarily.',
|