| 1 |
/** |
| 2 |
* contractFileIO: filesystem-access layer for the contract validation gate. |
| 3 |
* |
| 4 |
* Pure data-access concern. Reads files and directories from disk and parses |
| 5 |
* their contents. Contains no business/validation logic; callers in |
| 6 |
* validate-contracts.js decide what the read data means for contract validity. |
| 7 |
* |
| 8 |
* Exposes: |
| 9 |
* fileExists(p) -> boolean |
| 10 |
* pathExists(p) -> boolean |
| 11 |
* filesEqual(left, right) -> boolean |
| 12 |
* loadJson(p) -> parsed JSON (throws on bad JSON) |
| 13 |
* findJsonSchemaFiles(dir) -> string[] of *.schema.json paths |
| 14 |
* fileContainsAnnotation(filePath, id, name?) -> boolean |
| 15 |
* fileContainsParityAnnotation(filePath, id) -> boolean |
| 16 |
*/ |
| 17 |
// allow-no-test-found: file-IO layer of the contract-validation CLI gate; it is the data-access half of validate-contracts.js (its only consumer) and is exercised whenever that gate runs over the real contracts/schemas tree. There is no isolated JS unit spec because it only wraps fs/path reads, with no business logic to assert in isolation. |
| 18 |
|
| 19 |
const fs = require("fs"); |
| 20 |
const path = require("path"); |
| 21 |
|
| 22 |
function fileExists(p) { |
| 23 |
try { |
| 24 |
return fs.statSync(p).isFile(); |
| 25 |
} catch { |
| 26 |
return false; // allow-silent-catch: stat failure means file does not exist |
| 27 |
} |
| 28 |
} |
| 29 |
|
| 30 |
function pathExists(p) { |
| 31 |
try { |
| 32 |
fs.statSync(p); |
| 33 |
return true; |
| 34 |
} catch { |
| 35 |
return false; // allow-silent-catch: stat failure means path is unavailable |
| 36 |
} |
| 37 |
} |
| 38 |
|
| 39 |
function filesEqual(left, right) { |
| 40 |
return fs.readFileSync(left).equals(fs.readFileSync(right)); |
| 41 |
} |
| 42 |
|
| 43 |
function loadJson(p) { |
| 44 |
return JSON.parse(fs.readFileSync(p, "utf8")); |
| 45 |
} |
| 46 |
|
| 47 |
function findJsonSchemaFiles(dir) { |
| 48 |
const results = []; |
| 49 |
if (!fs.existsSync(dir)) return results; |
| 50 |
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { |
| 51 |
const full = path.join(dir, entry.name); |
| 52 |
if (entry.isDirectory()) { |
| 53 |
results.push(...findJsonSchemaFiles(full)); |
| 54 |
} else if (entry.name.endsWith(".schema.json")) { |
| 55 |
results.push(full); |
| 56 |
} |
| 57 |
} |
| 58 |
return results; |
| 59 |
} |
| 60 |
|
| 61 |
function fileContainsAnnotation(filePath, contractId, annotationName = "contract") { |
| 62 |
const content = fs.readFileSync(filePath, "utf8"); |
| 63 |
const pattern = new RegExp(`@${annotationName}\\s+${contractId.replace(/-/g, "\\-")}\\b`); |
| 64 |
return pattern.test(content); |
| 65 |
} |
| 66 |
|
| 67 |
function fileContainsParityAnnotation(filePath, contractId) { |
| 68 |
const content = fs.readFileSync(filePath, "utf8"); |
| 69 |
const pattern = new RegExp(`@parityTest\\s+${contractId.replace(/-/g, "\\-")}\\b`); |
| 70 |
return pattern.test(content); |
| 71 |
} |
| 72 |
|
| 73 |
module.exports = { |
| 74 |
fileExists, |
| 75 |
pathExists, |
| 76 |
filesEqual, |
| 77 |
loadJson, |
| 78 |
findJsonSchemaFiles, |
| 79 |
fileContainsAnnotation, |
| 80 |
fileContainsParityAnnotation, |
| 81 |
}; |
| 82 |
|