Skip to content
Snippets Groups Projects

Check user configuration in CreateDockerWorkerVersion

Merged ml bonhomme requested to merge validate-worker-config into master
1 file
+ 95
486
Compare changes
  • Side-by-side
  • Inline
@@ -549,9 +549,13 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
(self.user.id, Role.Admin.value)
])
# test version user configuration
# Test user configuration
def test_create_version_valid_user_configuration(self):
test_model = Model.objects.create(
name="Generic model",
public=False,
)
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
@@ -564,6 +568,12 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
"user_configuration": {
"demo_integer": {"title": "Demo Integer", "type": "int", "required": True, "default": 1},
"demo_boolean": {"title": "Demo Boolean", "type": "bool", "required": False, "default": True},
"demo_dict": {"title": "Demo Dict", "type": "dict", "required": True, "default": {"a": "b", "c": "d"}},
"demo_choice": {"title": "Decisions", "type": "enum", "required": True, "default": 1, "choices": [1, 2, 3]},
"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "int", "default": [1, 2, 3, 4]},
"boolean_list": {"title": "It's a list of booleans", "type": "list", "required": False, "subtype": "bool", "default": [True, False, False]},
"demo_model": {"title": "Model for training", "type": "model", "required": True},
"other_model": {"title": "Model the second", "type": "model", "default": str(test_model.id)}
}
},
},
@@ -583,109 +593,20 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
"type": "bool",
"required": False,
"default": True
}
}
})
def test_create_version_valid_user_configuration_dict(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_dict": {"title": "Demo Dict", "type": "dict", "required": True, "default": {"a": "b", "c": "d"}},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["configuration"], {
"user_configuration": {
"demo_dict": {
"title": "Demo Dict",
"type": "dict",
"required": True,
"default": {"a": "b", "c": "d"}
}
}
})
def test_create_version_user_configuration_dict_strings_only(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_dict": {"title": "Demo Dict", "type": "dict", "required": True, "default": {"a": ["12", "13"], "c": "d"}},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_create_version_valid_user_configuration_enum(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_choice": {"title": "Decisions", "type": "enum", "required": True, "default": 1, "choices": [1, 2, 3]}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["configuration"], {
"user_configuration": {
"demo_choice": {
"title": "Decisions",
"type": "enum",
"required": True,
"default": 1,
"choices": [1, 2, 3]
}
}
})
def test_create_version_valid_user_configuration_list(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "int", "default": [1, 2, 3, 4]},
"boolean_list": {"title": "It's a list of booleans", "type": "list", "required": False, "subtype": "bool", "default": [True, False, False]}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["configuration"], {
"user_configuration": {
"demo_list": {
"title": "Demo List",
"type": "list",
@@ -699,35 +620,7 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
"subtype": "bool",
"required": False,
"default": [True, False, False]
}
}
})
def test_create_version_valid_user_configuration_model(self):
test_model = Model.objects.create(
name="Generic model",
public=False,
)
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_model": {"title": "Model for training", "type": "model", "required": True},
"other_model": {"title": "Model the second", "type": "model", "default": str(test_model.id)}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["configuration"], {
"user_configuration": {
"demo_model": {
"title": "Model for training",
"type": "model",
@@ -741,354 +634,91 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
}
})
def test_create_version_invalid_user_configuration_list_requires_subtype(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_list": {"title": "Demo List", "type": "list", "required": True, "default": [1, 2, 3, 4]},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"demo_list": {
"subtype": ['The "subtype" field must be set for "list" type properties.']
}
}]
}
})
def test_create_version_invalid_user_configuration_list_wrong_default(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "int", "default": 12},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"demo_list": {
"default": ["This is not a valid value for a field of type list."]
}
}]
}
})
def test_create_version_invalid_user_configuration_list_wrong_subtype(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "dict", "default": [1, 2, 3, 4]},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"demo_list": {
"subtype": ["Subtype can only be int, float, bool or string."]
}
}]
}
})
def test_create_version_invalid_user_configuration_list_wrong_default_subtype(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "int", "default": [1, 2, "three", 4]},
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"demo_list": {
"default": ["All items in the default value must be of type int."]
}
}]
}
})
def test_create_version_invalid_user_configuration_not_list_choices(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_choice": {"title": "Decisions", "type": "enum", "required": True, "default": 1, "choices": "eeee"}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"demo_choice": {
"choices": ['Expected a list of items but got type "str".']
}
}]
}
})
def test_create_version_invalid_user_configuration_not_dict(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": "non"
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {"user_configuration": [['Expected a dictionary of items but got type "str".']]}})
def test_create_version_invalid_user_configuration_item_not_dict(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"secrets": ["aaaaaa"]
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {
"user_configuration": [{
"secrets":
{"__all__": ["User configuration field definitions should be of type dict, not list."]}
}]
}})
def test_create_version_invalid_user_configuration_wrong_field_type(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"something": {
"title": "some thing",
"type": "uh oh",
"required": 2
}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"something": {
"required": ["Must be a valid boolean."],
"type": ["Value is not of type UserConfigurationFieldType"]
}
}]
}
})
def test_create_version_invalid_user_configuration_wrong_default_type(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"one_float": {
"title": "a float",
"type": "float",
"default": "bonjour",
"required": True
}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"one_float": {
"default": ["This is not a valid value for a field of type float."]
}
}]
}
})
def test_create_version_invalid_user_configuration_choices_no_enum(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"something": {
"title": "some thing",
"type": "int",
"required": False,
"choices": [1, 2, 3]
}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [{
"something": {
"choices": ['The "choices" field can only be set for an "enum" type property.']
}
}]
}
})
def test_create_version_invalid_user_configuration_missing_key(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_integer": {"type": "int", "required": True, "default": 1}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.json(),
{
"configuration": {
"user_configuration": [{
"demo_integer": {
"title": ["This field is required."]
}
}]
}
}
)
def test_create_invalid_user_configuration(self):
cases = [
(
"non",
['Expected a dictionary of items but got type "str".']
),
(
{"demo_list": {"title": "Demo List", "type": "list", "required": True, "default": [1, 2, 3, 4]}},
{"demo_list": {
"subtype": ['The "subtype" field must be set for "list" type properties.']
}}
),
(
{"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "dict", "default": [1, 2, 3, 4]}},
{"demo_list": {
"subtype": ["Subtype can only be int, float, bool or string."]
}}
),
(
{"demo_list": {"title": "Demo List", "type": "list", "required": True, "subtype": "int", "default": [1, 2, "three", 4]}},
{"demo_list": {
"default": ["All items in the default value must be of type int."]
}}
),
(
{"demo_choice": {"title": "Decisions", "type": "enum", "required": True, "default": 1, "choices": "eeee"}},
{"demo_choice": {
"choices": ['Expected a list of items but got type "str".']
}}
),
(
{"secrets": ["aaaaaa"]},
{"secrets": {"__all__": ["User configuration field definitions should be of type dict, not list."]}}
),
(
{"something": {"title": "some thing", "type": "uh oh", "required": 2}},
{"something": {
"required": ["Must be a valid boolean."],
"type": ["Value is not of type UserConfigurationFieldType"]
}}
),
(
{"something": {"title": "some thing", "type": "int", "required": False, "choices": [1, 2, 3]}},
{"something": {
"choices": ['The "choices" field can only be set for an "enum" type property.']
}}
),
(
{"demo_integer": {"type": "int", "required": True, "default": 1}},
{"demo_integer": {"title": ["This field is required."]}}
),
(
{"demo_integer": {"title": "an integer", "type": "int", "required": True, "default": 1, "some_key": "oh no"}},
{"demo_integer": {
"some_key": ["Configurable properties can only be defined using the following keys: title, type, required, default, subtype, choices."]
}}
),
(
{"param": {"title": "Model to train", "type": "model", "default": "12341234-1234-1234-1234-123412341234"}},
{"param": {"default": ["Model 12341234-1234-1234-1234-123412341234 not found."]}}
)
]
def test_create_version_invalid_user_configuration_invalid_key(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"demo_integer": {
"title": "an integer",
"type": "int",
"required": True,
"default": 1,
"some_key": "oh no",
}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [
{
"demo_integer": {
"some_key": ["Configurable properties can only be defined using the following keys: title, type, required, default, subtype, choices."]
}
for user_configuration, error in cases:
with self.subTest(user_configuration=user_configuration, error=error):
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": user_configuration
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
"configuration": {
"user_configuration": [error]
}
]
}
})
})
def test_create_version_invalid_user_configuration_default_value(self):
self.client.force_login(self.user)
@@ -1102,7 +732,8 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
({"type": "string", "default": 1}, "string"),
({"type": "dict", "default": ["a", "b"]}, "dict"),
({"type": "model", "default": "gigi hadid"}, "model"),
({"type": "model", "default": False}, "model")
({"type": "model", "default": False}, "model"),
({"type": "list", "subtype": "int", "default": 12}, "list")
]
for params, expected in cases:
with self.subTest(**params):
@@ -1123,25 +754,3 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {"user_configuration": [{"param": {"default": [f"This is not a valid value for a field of type {expected}."]}}]}})
def test_create_version_user_configuration_model_default_doesnt_exist(self):
self.client.force_login(self.user)
response = self.client.post(
reverse("api:version-from-docker"),
data={
"docker_image_iid": "a_docker_image",
"repository_url": self.repo.url,
"revision_hash": "new_revision_hash",
"worker_slug": self.worker.slug,
"configuration": {
"user_configuration": {
"param": {"title": "Model to train", "type": "model", "default": "12341234-1234-1234-1234-123412341234"}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {"configuration": {"user_configuration": [{"param": {"default": [
"Model 12341234-1234-1234-1234-123412341234 not found."
]}}]}})
Loading