blob: 034c2fedf27ba123c4c3da0aaf1e0b22a5846971 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
import 'dart:io';
import 'package:cli_config/cli_config.dart';
import 'package:test/test.dart';
import 'helpers.dart';
void main() {
test('optionalStringList', () {
const path1 = 'path/in/cli_arguments/';
const path2 = 'path/in/cli_arguments_2/';
const path3 = 'path/in/environment/';
const path4 = 'path/in/environment_2/';
const path5 = 'path/in/config_file/';
const path6 = 'path/in/config_file_2/';
final config = Config.fromConfigFileContents(
commandLineDefines: [
'build.out_dir=$path1',
'build.out_dir=$path2',
],
environment: {
'BUILD__OUT_DIR': '$path3:$path4',
},
fileContents: jsonEncode(
{
'build': {
'out_dir': [
path5,
path6,
],
}
},
),
);
{
final result = config.optionalStringList(
'build.out_dir',
combineAllConfigs: true,
splitEnvironmentPattern: ':',
);
expect(result, [path1, path2, path3, path4, path5, path6]);
}
{
final result = config.optionalStringList(
'build.out_dir',
combineAllConfigs: false,
splitEnvironmentPattern: ':',
);
expect(result, [path1, path2]);
}
});
test('optionalString cli precedence', () {
const path1 = 'path/in/cli_arguments/';
const path2 = 'path/in/environment/';
const path3 = 'path/in/config_file/';
final config = Config.fromConfigFileContents(
commandLineDefines: [
'build.out_dir=$path1',
],
environment: {
'BUILD__OUT_DIR': path2,
},
fileContents: jsonEncode(
{
'build': {
'out_dir': path3,
}
},
),
);
final result = config.optionalString(
'build.out_dir',
);
expect(result, path1);
});
test('optionalString environment precedence', () {
const path2 = 'path/in/environment/';
const path3 = 'path/in/config_file/';
final config = Config.fromConfigFileContents(
commandLineDefines: [],
environment: {
'BUILD__OUT_DIR': path2,
},
fileContents: jsonEncode(
{
'build': {
'out_dir': path3,
}
},
),
);
final result = config.optionalString(
'build.out_dir',
);
expect(result, path2);
});
test('optionalString config file', () {
const path3 = 'path/in/config_file/';
final config = Config.fromConfigFileContents(
commandLineDefines: [],
environment: {},
fileContents: jsonEncode(
{
'build': {
'out_dir': path3,
}
},
),
);
final result = config.optionalString(
'build.out_dir',
);
expect(result, path3);
});
test('optionalBool define', () {
final config = Config(
commandLineDefines: ['my_bool=true'],
);
expect(config.optionalBool('my_bool'), true);
});
test('optionalBool environment', () {
final config = Config(
environment: {
'MY_BOOL': 'true',
},
);
expect(config.optionalBool('my_bool'), true);
});
test('optionalBool file', () {
final config = Config.fromConfigFileContents(
fileContents: jsonEncode(
{'my_bool': true},
),
);
expect(config.optionalBool('my_bool'), true);
});
test('Read file and parse CLI args', () async {
final temp = await Directory.systemTemp.createTemp();
final configFile = File.fromUri(temp.uri.resolve('config.yaml'));
await configFile.writeAsString(jsonEncode(
{
'build': {
'out_dir': 'path/in/config_file/',
}
},
));
final config = await Config.fromArgs(
args: [
'--config',
configFile.path,
'-Dbuild.out_dir=path/in/cli_arguments/',
],
environment: {
'BUILD__OUT_DIR': 'path/in/environment',
},
);
final result = config.optionalString('build.out_dir');
expect(result, 'path/in/cli_arguments/');
});
test('Resolve config file path relative to config file', () async {
final temp = await Directory.systemTemp.createTemp();
final tempUri = temp.uri;
final configUri = tempUri.resolve('config.yaml');
final configFile = File.fromUri(configUri);
const relativePath = 'path/in/config_file/';
final resolvedPath = configUri.resolve(relativePath);
await configFile.writeAsString(jsonEncode(
{
'build': {
'out_dir': relativePath,
}
},
));
final config = await Config.fromArgs(
args: [
'--config',
configFile.path,
],
);
final result = config.optionalPath('build.out_dir');
expect(result!.path, resolvedPath.path);
});
test('provide pre-parsed config', () {
const path3 = 'path/in/config_file/';
final config = Config(
commandLineDefines: [],
environment: {},
fileParsed: {
'build': {
'out_dir': path3,
}
},
);
final result = config.optionalString('build.out_dir');
expect(result, path3);
});
test('path exists', () async {
await inTempDir((tempUri) async {
final tempFileUri = tempUri.resolve('file.ext');
await File.fromUri(tempFileUri).create();
final nonExistUri = tempUri.resolve('foo.ext');
final config = Config(
commandLineDefines: [],
environment: {},
fileParsed: {
'build': {
'out_dir': tempUri.toFilePath(),
'file': tempFileUri.toFilePath(),
'non_exist': nonExistUri.toFilePath(),
}
},
);
final result = config.optionalPath('build.out_dir', mustExist: true);
expect(result, tempUri);
final result2 = config.optionalPath('build.file', mustExist: true);
expect(result2, tempFileUri);
expect(
() => config.optionalPath('build.non_exist', mustExist: true),
throwsFormatException,
);
});
});
test('wrong CLI key format', () {
expect(
() => Config(commandLineDefines: ['CAPITALIZED=value']),
throwsFormatException,
);
});
test('CLI two values when expecting one', () {
final config = Config(commandLineDefines: ['key=value', 'key=value2']);
expect(
() => config.string('key'),
throwsFormatException,
);
});
test('CLI split optionalStringList', () {
final config = Config(commandLineDefines: ['key=value;value2']);
final value = config.optionalStringList('key', splitCliPattern: ';');
expect(value, ['value', 'value2']);
});
test('CLI path', () {
final uri = Uri.file('some/path.ext');
final config = Config(commandLineDefines: ['key=${uri.path}']);
final value = config.optionalPath('key');
expect(value, uri);
});
test('CLI path list', () {
final uri = Uri.file('some/path.ext');
final uri2 = Uri.file('some/directory/');
final config = Config(commandLineDefines: ['key=${uri.path}:${uri2.path}']);
final value = config.optionalPathList('key', splitCliPattern: ':');
expect(value, [uri, uri2]);
});
test('toString', () {
final config = Config(
commandLineDefines: ['key=foo'],
environment: {'key': 'bar'},
fileParsed: {'key': 'baz'},
);
config.toString();
});
test('Missing nonullable throws FormatException', () {
final config = Config.fromConfigFileContents();
expect(() => config.bool('key'), throwsFormatException);
expect(() => config.string('key'), throwsFormatException);
expect(() => config.path('key'), throwsFormatException);
});
test('string not validValue throws FormatException', () {
final config = Config(environment: {'foo': 'bar'});
expect(
() => config.string('foo', validValues: ['not_bar']),
throwsFormatException,
);
});
test('optionalString validValues', () {
final config = Config();
expect(config.optionalString('foo', validValues: ['bar']), isNull);
});
test('valueOf file source', () {
final config = Config(fileParsed: {
'key': {'some': 'map'}
});
final value = config.valueOf<Map<dynamic, dynamic>>('key');
expect(value, {'some': 'map'});
});
test('valueOf command line source', () {
final config = Config(commandLineDefines: [
'string_key=value',
'bool_key=true',
'string_list_key=value1',
'string_list_key=value2',
]);
expect(config.valueOf<String>('string_key'), 'value');
expect(config.valueOf<bool>('bool_key'), true);
expect(
config.valueOf<List<String>>('string_list_key'),
['value1', 'value2'],
);
});
test('environment split optionalStringList', () {
final config = Config(environment: {'key': 'value;value2'});
final value =
config.optionalStringList('key', splitEnvironmentPattern: ';');
expect(value, ['value', 'value2']);
});
test('environment non split optionalStringList', () {
final config = Config(environment: {'key': 'value'});
final value = config.optionalStringList('key');
expect(value, ['value']);
});
test('environment path', () {
final uri = Uri.file('some/path.ext');
final config = Config(environment: {'key': uri.path});
final value = config.optionalPath('key');
expect(value, uri);
});
test('environment path list', () {
final uri = Uri.file('some/path.ext');
final uri2 = Uri.file('some/directory/');
final config = Config(environment: {'key': '${uri.path}:${uri2.path}'});
final value = config.optionalPathList('key', splitEnvironmentPattern: ':');
expect(value, [uri, uri2]);
});
test('Unexpected config file contents', () {
expect(() => Config.fromConfigFileContents(fileContents: 'asdf'),
throwsFormatException);
expect(() => Config.fromConfigFileContents(fileContents: "['asdf']"),
throwsFormatException);
expect(
() => Config.fromConfigFileContents(
fileContents: '''
WRONGKEY:
1
'''
.trim()),
throwsFormatException,
);
expect(
() => Config.fromConfigFileContents(
fileContents: '''
1: 'asdf'
'''
.trim()),
throwsFormatException,
);
});
test('file config try to access object as wrong type', () {
final config = Config.fromConfigFileContents(fileContents: '''foo:
bar:
true
''');
expect(config.bool('foo.bar'), true);
expect(() => config.bool('foo.bar.baz'), throwsFormatException);
expect(() => config.string('foo.bar'), throwsFormatException);
});
test('file config path list unresolved', () {
final uri = Uri.file('some/path.ext');
final uri2 = Uri.file('some/directory/');
final config = Config(fileParsed: {
'key': [uri.path, uri2.path]
});
final value = config.optionalPathList('key', resolveUri: false);
expect(value, [uri, uri2]);
});
test('file config path list resolved', () {
final configUri = Uri.file('path/to/config.json');
final uri = Uri.file('some/path.ext');
final uri2 = Uri.file('some/directory/');
final config = Config(
fileSourceUri: configUri,
fileParsed: {
'key': [uri.path, uri2.path]
},
);
final value = config.optionalPathList('key', resolveUri: true);
expect(value, [configUri.resolveUri(uri), configUri.resolveUri(uri2)]);
});
test('resolveUri in working directory', () {
final systemTemp = Directory.systemTemp.uri;
final tempUri = systemTemp.resolve('x/y/z/');
final relativePath = Uri.file('a/b/c/d.ext');
final absolutePath = tempUri.resolveUri(relativePath);
final config = Config(
commandLineDefines: ['path=${relativePath.path}'],
workingDirectory: tempUri,
);
expect(config.optionalPath('path', mustExist: false, resolveUri: true),
absolutePath);
});
test('ints', () {
final config = Config(
commandLineDefines: ['cl=1', 'not_parsable=asdf'],
environment: {
'env': '2',
'not_parsable2': 'asfd',
},
fileParsed: {'file': 3},
);
expect(config.int('cl'), 1);
expect(config.optionalInt('env'), 2);
expect(config.optionalInt('file'), 3);
expect(config.optionalInt('nothing'), null);
expect(() => config.optionalInt('not_parsable'), throwsFormatException);
expect(() => config.optionalInt('not_parsable2'), throwsFormatException);
});
test('doubles', () {
final config = Config(
commandLineDefines: ['cl=1.1', 'not_parsable=asdf'],
environment: {
'env': '2.2',
'not_parsable2': 'asfd',
},
fileParsed: {'file': 3.3},
);
expect(config.double('cl'), 1.1);
expect(config.optionalDouble('env'), 2.2);
expect(config.optionalDouble('file'), 3.3);
expect(config.optionalDouble('nothing'), null);
expect(() => config.optionalDouble('not_parsable'), throwsFormatException);
expect(() => config.optionalDouble('not_parsable2'), throwsFormatException);
});
test('stringList and optionalStringList', () {
{
final config = Config(
fileParsed: {},
);
expect(config.optionalStringList('my_list'), null);
expect(() => config.stringList('my_list'), throwsFormatException);
}
{
final config = Config(
fileParsed: {'my_list': <String>[]},
);
expect(config.optionalStringList('my_list'), <String>[]);
expect(config.stringList('my_list'), <String>[]);
}
});
test('pathList and optionalPathList', () {
{
final config = Config(
fileParsed: {},
);
expect(config.optionalPathList('my_list'), null);
expect(() => config.pathList('my_list'), throwsFormatException);
}
{
final config = Config(
fileParsed: {'my_list': <String>[]},
);
expect(config.optionalPathList('my_list'), <String>[]);
expect(config.pathList('my_list'), <String>[]);
}
});
test('non-string key maps', () {
// This is valid in YAML (not in JSON).
//
// Such values cannot be accessed with our hierarchical keys, but they can
// be accessed with [Config.valueOf].
final config = Config(
fileParsed: {
'my_non_string_key_map': {
1: 'asdf',
2: 'foo',
},
},
);
expect(
config.valueOf<Map<Object, Object?>>('my_non_string_key_map'),
{
1: 'asdf',
2: 'foo',
},
);
});
test('null values in maps', () {
final config = Config(
fileParsed: {
'my_non_string_key_map': {
'x': null,
'y': 42,
},
},
);
expect(
config.valueOf<Map<Object, Object?>>('my_non_string_key_map'),
{
'x': null,
'y': 42,
},
);
});
}