1 # ============LICENSE_START=======================================================
3 # ================================================================================
4 # Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved.
5 # ================================================================================
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 # ============LICENSE_END=========================================================
19 # ECOMP is a trademark and service mark of AT&T Intellectual Property.
21 # -*- coding: utf-8 -*-
23 Tests the mock catalog
28 from dcae_cli.catalog.mock.schema import validate_component, validate_format,apply_defaults_docker_config, apply_defaults
29 from dcae_cli.catalog.mock import schema
30 from dcae_cli.util.exc import DcaeException
36 "name": "asimov.format.integerClassification",
38 "description": "Represents a single classification from a machine learning model - just a test version"
40 "dataformatversion": "1.0.0",
42 "$schema": "http://json-schema.org/draft-04/schema#",
49 "additionalProperties": false
59 "name": "asimov.component.kpi_anomaly",
60 "description": "Classifies VNF KPI data as anomalous",
61 "component_type": "docker"
66 "format": "dcae.vnf.kpi",
72 "format":"std.format_one",
75 "type": "message router"
80 "format": "asimov.format.integerClassification",
82 "config_key": "prediction",
86 "format":"std.format_one",
89 "type": "message router"
97 "route": "/score-vnf",
99 "format": "dcae.vnf.kpi",
103 "format": "asimov.format.integerClassification",
113 "description": "Probability threshold to exceed to be anomalous",
114 "designer_editable": false,
115 "sourced_at_deployment": false,
116 "policy_editable": false
121 "uri": "somedockercontainerpath",
122 "type": "docker image"
128 "endpoint": "/health"
134 cdap_component_test = r'''
137 "name":"std.cdap_comp",
139 "description":"cdap test component",
140 "component_type":"cdap"
145 "format":"std.format_one",
151 "format":"std.format_one",
154 "type": "message router"
159 "format":"std.format_two",
165 "format":"std.format_one",
168 "type": "message router"
179 "format":"std.format_one",
183 "format":"std.format_two",
186 "service_name":"baphomet",
187 "service_endpoint":"rises",
197 "uri": "somecdapjarurl",
203 "artifact_name" : "HelloWorld",
204 "artifact_version" : "3.4.3",
206 {"program_type" : "flows", "program_id" : "WhoFlow"},
207 {"program_type" : "services", "program_id" : "Greeting"}
215 def test_basic(mock_cli_config):
216 validate_component(json.loads(component_test))
217 validate_format(json.loads(format_test))
218 validate_component(json.loads(cdap_component_test))
220 # Test with DR publishes for cdap
221 dr_publishes = { "format":"std.format_one", "version":"1.0.0",
222 "config_key":"pub3", "type": "data router" }
223 cdap_valid = json.loads(cdap_component_test)
224 cdap_valid["streams"]["publishes"].append(dr_publishes)
226 # Test with DR subscribes for cdap
227 cdap_invalid = json.loads(cdap_component_test)
228 ss = cdap_invalid["streams"]["subscribes"][0]
229 ss["type"] = "data_router"
230 ss["config_key"] = "nada"
231 cdap_invalid["streams"]["subscribes"][0] = ss
233 with pytest.raises(DcaeException):
234 validate_component(cdap_invalid)
238 def test_validate_docker_config(mock_cli_config):
240 def compose_spec(config):
241 spec = json.loads(component_test)
242 spec["auxilary"] = config
245 good_docker_configs = [
249 "endpoint": "/health",
257 "script": "curl something"
261 for good_config in good_docker_configs:
262 spec = compose_spec(good_config)
263 assert validate_component(spec) == None
265 bad_docker_configs = [
282 for bad_config in bad_docker_configs:
283 with pytest.raises(DcaeException):
284 spec = compose_spec(bad_config)
285 validate_component(spec)
288 def test_validate_cdap_config(mock_cli_config):
290 def compose_spec(config):
291 spec = json.loads(cdap_component_test)
292 spec["auxilary"] = config
295 good_cdap_configs = [
297 "streamname":"streamname",
298 "artifact_version":"6.6.6",
299 "artifact_name" : "testname",
303 "streamname":"streamname",
304 "artifact_version":"6.6.6",
305 "artifact_name" : "testname",
306 "programs" : [{"program_type" : "flows", "program_id" : "flow_id"}],
307 "program_preferences" : [{"program_type" : "flows", "program_id" : "flow_id", "program_pref" : {"he" : "shall rise"}}],
308 "namespace" : "this should be an optional field",
309 "app_preferences" : {"he" : "shall rise"}
313 for good_config in good_cdap_configs:
314 spec = compose_spec(good_config)
315 assert validate_component(spec) == None
319 {"YOU HAVE" : "ALWAYS FAILED ME"}
322 for bad_config in bad_cdap_configs:
323 with pytest.raises(DcaeException):
324 spec = compose_spec(bad_config)
325 validate_component(bad_config)
328 def test_apply_defaults():
329 definition = { "length": { "default": 10 }, "duration": { "default": "10s" } }
331 # Test: Add expected properties
333 actual = apply_defaults(definition, properties)
334 assert actual == { "length": 10, "duration": "10s" }
336 # Test: Don't mess with existing values
337 properties = { "length": 100, "duration": "100s" }
338 actual = apply_defaults(definition, properties)
339 assert actual == properties
341 # Test: No defaults to apply
342 definition = { "length": {}, "duration": {} }
343 properties = { "width": 100 }
344 actual = apply_defaults(definition, properties)
345 assert actual == properties
347 # Test: Nested object
348 definition = { "length": { "default": 10 }, "duration": { "default": "10s" },
349 "location": { "properties": { "lat": { "default": "40" },
350 "long": { "default": "75" }, "alt": {} } } }
351 actual = apply_defaults(definition, {})
352 assert actual == {'duration': '10s', 'length': 10,
353 'location': {'lat': '40', 'long': '75'}}
356 def test_apply_defaults_docker_config(mock_cli_config):
357 # Test: Adding of missing expected properties for http
358 dc = { "healthcheck": { "type": "http", "endpoint": "/foo" } }
359 actual = apply_defaults_docker_config(dc)
361 assert "interval" in actual["healthcheck"]
362 assert "timeout" in actual["healthcheck"]
364 # Test: Adding of missing expected properties for script
365 dc = { "healthcheck": { "type": "script", "script": "/bin/do-something" } }
366 actual = apply_defaults_docker_config(dc)
368 assert "interval" in actual["healthcheck"]
369 assert "timeout" in actual["healthcheck"]
371 # Test: Expected properties already exist
372 dc = { "healthcheck": { "type": "http", "endpoint": "/foo",
373 "interval": "10000s", "timeout": "100000s" } }
374 actual = apply_defaults_docker_config(dc)
377 # Test: Never should happen
378 dc = { "healthcheck": { "type": "bogus" } }
379 actual = apply_defaults_docker_config(dc)
385 "$schema": "http://json-schema.org/draft-04/schema#",
386 "title": "Test schema",
389 "foo": { "type": "string" },
390 "bar": { "type": "integer" }
392 "required": ["foo", "bar"]
395 good_path = "/correct_path"
397 def fetch_schema(path):
398 if path == good_path:
401 raise schema.FetchSchemaError("Schema not found")
405 good_instance = { "foo": "hello", "bar": 1776 }
407 schema._validate(fetch_schema, good_path, good_instance)
409 # Error from validating
413 with pytest.raises(DcaeException):
414 schema._validate(fetch_schema, good_path, bad_instance)
416 # Error from fetching
418 bad_path = "/wrong_path"
420 with pytest.raises(DcaeException):
421 schema._validate(fetch_schema, bad_path, good_instance)