From 6cc3fb3d0466e89b67be271009a2fc95f3ed41ca Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 24 Aug 2023 18:29:54 +0200 Subject: harness: modernize some tests --- packages/taler-util/src/talerconfig.ts | 96 ++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 23 deletions(-) (limited to 'packages/taler-util') diff --git a/packages/taler-util/src/talerconfig.ts b/packages/taler-util/src/talerconfig.ts index 59c789cae..948ccb9c4 100644 --- a/packages/taler-util/src/talerconfig.ts +++ b/packages/taler-util/src/talerconfig.ts @@ -39,10 +39,20 @@ export class ConfigError extends Error { } } +enum EntryOrigin { + // From a default file + Default = 1, + // Loaded from file or string + Loaded = 2, + // Changed after loading + Changed = 3, +} + interface Entry { value: string; sourceLine: number; sourceFile: string; + origin: EntryOrigin; } interface Section { @@ -195,6 +205,7 @@ export interface LoadOptions { export interface StringifyOptions { diagnostics?: boolean; + excludeDefaults?: boolean; } export interface LoadedFile { @@ -282,7 +293,11 @@ export class Configuration { private nestLevel = 0; - private loadFromFilename(filename: string, opts: LoadOptions = {}): void { + private loadFromFilename( + filename: string, + isDefaultSource: boolean, + opts: LoadOptions = {}, + ): void { filename = expandPath(filename); const checkCycle = () => { @@ -309,7 +324,7 @@ export class Configuration { const oldNestLevel = this.nestLevel; this.nestLevel += 1; try { - this.loadFromString(s, { + this.internalLoadFromString(s, isDefaultSource, { ...opts, filename: filename, }); @@ -318,7 +333,11 @@ export class Configuration { } } - private loadGlob(parentFilename: string, fileglob: string): void { + private loadGlob( + parentFilename: string, + isDefaultSource: boolean, + fileglob: string, + ): void { const resolvedParent = nodejs_fs.realpathSync(parentFilename); const parentDir = nodejs_path.dirname(resolvedParent); @@ -339,12 +358,16 @@ export class Configuration { for (const f of files) { if (globMatch(tail, f)) { const fullPath = nodejs_path.join(head, f); - this.loadFromFilename(fullPath); + this.loadFromFilename(fullPath, isDefaultSource); } } } - private loadSecret(sectionName: string, filename: string): void { + private loadSecret( + sectionName: string, + filename: string, + isDefaultSource: boolean, + ): void { const sec = this.provideSection(sectionName); sec.secretFilename = filename; const otherCfg = new Configuration(); @@ -354,7 +377,7 @@ export class Configuration { sec.inaccessible = true; return; } - otherCfg.loadFromFilename(filename, { + otherCfg.loadFromFilename(filename, isDefaultSource, { banDirectives: true, }); const otherSec = otherCfg.provideSection(sectionName); @@ -363,7 +386,11 @@ export class Configuration { } } - loadFromString(s: string, opts: LoadOptions = {}): void { + private internalLoadFromString( + s: string, + isDefaultSource: boolean, + opts: LoadOptions = {}, + ): void { let lineNo = 0; const fn = opts.filename ?? ""; const reComment = /^\s*#.*$/; @@ -399,7 +426,10 @@ export class Configuration { ); } const arg = directiveMatch[2].trim(); - this.loadFromFilename(normalizeInlineFilename(opts.filename, arg)); + this.loadFromFilename( + normalizeInlineFilename(opts.filename, arg), + isDefaultSource, + ); break; } case "inline-secret": { @@ -419,7 +449,7 @@ export class Configuration { opts.filename, sp[1], ); - this.loadSecret(sp[0], secretFilename); + this.loadSecret(sp[0], secretFilename, isDefaultSource); break; } case "inline-matching": { @@ -429,7 +459,7 @@ export class Configuration { `invalid configuration, @inline-matching@ directive in ${fn}:${lineNo} can only be used from a file`, ); } - this.loadGlob(opts.filename, arg); + this.loadGlob(opts.filename, isDefaultSource, arg); break; } default: @@ -462,6 +492,7 @@ export class Configuration { value: val, sourceFile: opts.filename ?? "", sourceLine: lineNo, + origin: isDefaultSource ? EntryOrigin.Default : EntryOrigin.Loaded, }; continue; } @@ -471,6 +502,10 @@ export class Configuration { } } + loadFromString(s: string, opts: LoadOptions = {}): void { + return this.internalLoadFromString(s, false, opts); + } + private provideSection(section: string): Section { const secNorm = section.toUpperCase(); if (this.sectionMap[secNorm]) { @@ -496,6 +531,7 @@ export class Configuration { value, sourceLine: 0, sourceFile: "", + origin: EntryOrigin.Changed, }; } @@ -578,11 +614,11 @@ export class Configuration { ); } - loadFrom(dirname: string): void { + loadDefaultsFromDir(dirname: string): void { const files = nodejs_fs.readdirSync(dirname); for (const f of files) { const fn = nodejs_path.join(dirname, f); - this.loadFromFilename(fn); + this.loadFromFilename(fn, true); } } @@ -601,7 +637,7 @@ export class Configuration { if (!bc) { bc = "/usr/share/taler/config.d"; } - this.loadFrom(bc); + this.loadDefaultsFromDir(bc); } getDefaultConfigFilename(): string | undefined { @@ -631,11 +667,13 @@ export class Configuration { const cfg = new Configuration(); cfg.loadDefaults(); if (filename) { - cfg.loadFromFilename(filename); + cfg.loadFromFilename(filename, false); } else { const fn = cfg.getDefaultConfigFilename(); if (fn) { - cfg.loadFromFilename(fn); + // It's the default filename for the main config file, + // but we don't consider the values default values. + cfg.loadFromFilename(fn, false); } } cfg.hintEntrypoint = filename; @@ -657,13 +695,20 @@ export class Configuration { } for (const sectionName of Object.keys(this.sectionMap)) { const sec = this.sectionMap[sectionName]; - if (opts.diagnostics && sec.secretFilename) { - s += `# Secret section from ${sec.secretFilename}\n`; - s += `# Secret accessible: ${!sec.inaccessible}\n`; - } - s += `[${sectionName}]\n`; + let headerWritten = false; for (const optionName of Object.keys(sec.entries)) { const entry = this.sectionMap[sectionName].entries[optionName]; + if (opts.excludeDefaults && entry.origin === EntryOrigin.Default) { + continue; + } + if (!headerWritten) { + if (opts.diagnostics && sec.secretFilename) { + s += `# Secret section from ${sec.secretFilename}\n`; + s += `# Secret accessible: ${!sec.inaccessible}\n`; + } + s += `[${sectionName}]\n`; + headerWritten = true; + } if (entry !== undefined) { if (opts.diagnostics) { s += `# ${entry.sourceFile}:${entry.sourceLine}\n`; @@ -671,12 +716,17 @@ export class Configuration { s += `${optionName} = ${entry.value}\n`; } } - s += "\n"; + if (headerWritten) { + s += "\n"; + } } return s; } - write(filename: string): void { - nodejs_fs.writeFileSync(filename, this.stringify()); + write(filename: string, opts: { excludeDefaults?: boolean } = {}): void { + nodejs_fs.writeFileSync( + filename, + this.stringify({ excludeDefaults: opts.excludeDefaults }), + ); } } -- cgit v1.2.3