def load_one(self, schema, val, path): is_path = schema.kind == types.Kind.Path if isinstance(val, types.Var): if val.name not in self.variables: raise UnboundVariable(path) elif not does_substition_match(self.variables[val.name], schema.kind): raise TypeMismatch(path) return types.Value(types.Var(val.name, choices=schema.choices), is_path) elif isinstance(val, types.Call): if val.name not in self.functions: raise InvalidFunction(path) elif not does_substition_match( self.functions[val.name].return_type, schema.kind): raise TypeMismatch(path) return types.Value(types.Call(val.name, val.args), is_path) else: result = { types.Kind.Any: self.load_any, types.Kind.Bool: self.load_bool, types.Kind.String: self.load_string, types.Kind.Path: self.load_path, types.Kind.Array: self.load_array, types.Kind.Object: self.load_object, }[schema.kind](schema, val, path) if schema.choices is not None: if result.data not in schema.choices: raise InvalidChoice() return result
def test_object(self): schema = phase2.MODEL.parse("{ foo: string; }", 'type') result = phase2.Phase2.load(schema, {'foo': 'bar'}) self.assertEqual(result.foo, types.Value('bar')) result = phase2.Phase2.load(schema, {}) self.assertEqual(result.foo, types.Value(None)) with self.assertRaises(phase2.InvalidKey): phase2.Phase2.load(schema, {'bad-key': 'bar'})
def test_any(self): schema = phase2.MODEL.parse('any', 'type') result = phase2.Phase2.load(schema, 'myString') self.assertEqual(result, types.Value('myString')) result = phase2.Phase2.load(schema, True) self.assertEqual(result, types.Value(True)) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, None)
def test_string_array(self): schema = phase2.MODEL.parse('string[]', 'type') result = phase2.Phase2.load(schema, ['a', 'b']) self.assertEqual(result, [types.Value('a'), types.Value('b')]) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, 'foo') with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, [True]) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, ['foo', True])
def test_var_choices(self): class Context: def resolve(self, _): # pylint: disable=no-self-use return 'z' self.assertEqual( phase3.resolve_value( types.Value(types.Var('foo', choices=('x', 'z'))), Context()), 'z') with self.assertRaises(phase2.InvalidChoice): phase3.resolve_value( types.Value(types.Var('foo', choices=('x', 'y'))), Context())
def load_object(self, schema, val, path): if isinstance(val, types.Step): return self.load_step(schema, val, path) else: if not isinstance(val, collections.abc.Mapping): raise TypeMismatch(path) temp_obj = {} if schema.wildcard_key(): for key in val: subpath = path + [key] temp_obj[key] = self.load_one(schema.fields[Key('*')], val[key], subpath) else: for key in val: if Key(key) not in schema.fields: raise InvalidKey(path, key) subpath = path + [key] temp_obj[key] = self.load_one(schema.fields[Key(key)], val[key], subpath) for key in schema.fields: if key.name not in temp_obj and key.name != '*': temp_obj[key.name] = types.Value(None) if key.is_required: if key.name not in val: raise MissingKey(path) temp_obj = make_keys_safe(temp_obj) cls = attr.make_class('SchemaClass', list(temp_obj.keys())) return cls(**temp_obj)
def test_call_to_path(self): schema = phase2.MODEL.parse('path', 'type') result = phase2.Phase2.load(schema, types.Call('env', ('key', ))) self.assertEqual( result, types.Value(types.Call('env', ('key', )), is_path=True)) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, True)
def test_run(self): cls = attr.make_class('MockSet', ['a']) recipe = cls(types.Value('b')) steps = [types.Step('set', recipe, None)] ctx = phase3.Context() phase3.run(steps, ctx) self.assertEqual(ctx.variables, {'a': 'b'})
def test_path(self): class Context: def repath(self, path): # pylint: disable=no-self-use return os.path.join('/root', path) self.assertEqual( phase3.resolve_value(types.Value('foo', is_path=True), Context()), '/root/foo')
def test_var(self): schema = phase2.MODEL.parse('string', 'type') result = phase2.Phase2.load(schema, types.Var('x'), {'x': types.Kind.String}) self.assertEqual(result, types.Value(types.Var('x'))) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, types.Var('x'), {'x': types.Kind.Bool}) with self.assertRaises(phase2.UnboundVariable): phase2.Phase2.load(schema, types.Var('x'))
def test_var(self): this = self class Context: def resolve(self, var): # pylint: disable=no-self-use this.assertEqual(var.name, 'foo') return 'myResult' self.assertEqual( phase3.resolve_value(types.Value(types.Var('foo')), Context()), 'myResult')
def test_call(self): this = self class Context: def call(self, call): # pylint: disable=no-self-use this.assertEqual(call.name, 'foo') return 'myResult' self.assertEqual( phase3.resolve_value(types.Value(types.Call('foo', ())), Context()), 'myResult')
def load_path(self, _, val, path): # pylint: disable=no-self-use if not isinstance(val, str): raise TypeMismatch(path) return types.Value(val, is_path=True)
def test_object(self): cls = attr.make_class('Mock', ['foo']) obj = cls(types.Value('bar')) obj = phase3.resolve(obj, None) self.assertEqual(obj.foo, 'bar')
def test_list(self): lst = [types.Value('x'), types.Value('y')] self.assertEqual(phase3.resolve(lst, None), ['x', 'y'])
def test_call(self): ctx = phase3.Context() os.environ['FOO'] = 'bar' call = types.Call('env', ('FOO', )) self.assertEqual(phase3.resolve(types.Value(call), ctx), 'bar')
def test_var(self): ctx = phase3.Context() ctx.variables['foo'] = 'bar' self.assertEqual(phase3.resolve(types.Value(types.Var('foo')), ctx), 'bar')
def test_none(self): self.assertIs(phase3.resolve_value(types.Value(None), None), None)
def load_any(self, _, val, path): # pylint: disable=no-self-use if val is None: raise TypeMismatch(path) return types.Value(val)
def test_required_key(self): schema = phase2.MODEL.parse("{ required foo: string; }", 'type') result = phase2.Phase2.load(schema, {'foo': 'bar'}) self.assertEqual(result.foo, types.Value('bar')) with self.assertRaises(phase2.MissingKey): phase2.Phase2.load(schema, {})
def test_call(self): schema = phase2.MODEL.parse('string', 'type') result = phase2.Phase2.load(schema, types.Call('env', ('x', ))) self.assertEqual(result, types.Value(types.Call('env', ('x', )))) with self.assertRaises(phase2.InvalidFunction): phase2.Phase2.load(schema, types.Call('x', ()))
def test_string(self): self.assertEqual(phase3.resolve_value(types.Value('foo'), None), 'foo')
def test_wildcard(self): schema = phase2.MODEL.parse("{ *: string; }", 'type') phase2.Phase2.load(schema, {}) result = phase2.Phase2.load(schema, {'foo': 'bar'}) self.assertEqual(result.foo, types.Value('bar'))
def test_bool(self): self.assertEqual(phase3.resolve_value(types.Value(True), None), True) self.assertEqual(phase3.resolve_value(types.Value(False), None), False)
def test_path(self): schema = phase2.MODEL.parse('path', 'type') result = phase2.Phase2.load(schema, 'myPath') self.assertEqual(result, types.Value('myPath', is_path=True)) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, True)
def test_invalid_value(self): with self.assertRaises(TypeError): phase3.resolve_value(types.Value(object()), None)
def test_bool(self): schema = phase2.MODEL.parse('bool', 'type') result = phase2.Phase2.load(schema, True) self.assertEqual(result, types.Value(True)) with self.assertRaises(phase2.TypeMismatch): phase2.Phase2.load(schema, 'foo')
def test_primitive(self): self.assertEqual(phase3.resolve(types.Value(True), None), True) self.assertEqual(phase3.resolve(types.Value('foo'), None), 'foo')
def load_bool(self, _, val, path): # pylint: disable=no-self-use if not isinstance(val, bool): raise TypeMismatch(path) return types.Value(val)
def test_choice(self): schema = phase2.MODEL.parse("string choices('x', 'y')", 'type') result = phase2.Phase2.load(schema, 'x') self.assertEqual(result, types.Value('x')) with self.assertRaises(phase2.InvalidChoice): phase2.Phase2.load(schema, 'foo')