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
@@ -6,6 +6,7 @@ from rest_framework import status
from arkindex.ponos.models import Farm
from arkindex.process.models import FeatureUsage, GitRefType, Process, ProcessMode, Repository, Worker, WorkerType
from arkindex.project.tests import FixtureAPITestCase
from arkindex.training.models import Model
from arkindex.users.models import Role, Scope
@@ -128,6 +129,7 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertDictEqual(response.json(), {
"configuration": ['Expected a dictionary of items but got type "str".'],
"docker_image_iid": ["Not a valid string."],
"gpu_usage": ["Value is not of type FeatureUsage"],
"model_usage": ["Value is not of type FeatureUsage"],
@@ -546,3 +548,209 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
self.assertListEqual(list(new_repo.memberships.values_list("user", "level")), [
(self.user.id, Role.Admin.value)
])
# 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"),
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": "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)}
}
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["configuration"], {
"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",
"subtype": "int",
"required": True,
"default": [1, 2, 3, 4]
},
"boolean_list": {
"title": "It's a list of booleans",
"type": "list",
"subtype": "bool",
"required": False,
"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)
}
}
})
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."]}}
)
]
self.client.force_login(self.user)
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)
cases = [
({"type": "int", "default": False}, "int"),
({"type": "int", "default": True}, "int"),
({"type": "float", "default": False}, "float"),
({"type": "float", "default": True}, "float"),
({"type": "bool", "default": 0}, "bool"),
({"type": "bool", "default": 1}, "bool"),
({"type": "string", "default": 1}, "string"),
({"type": "dict", "default": ["a", "b"]}, "dict"),
({"type": "model", "default": "gigi hadid"}, "model"),
({"type": "model", "default": False}, "model"),
({"type": "list", "subtype": "int", "default": 12}, "list")
]
for params, expected in cases:
with self.subTest(**params):
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": "param", **params}
}
},
},
format="json",
)
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}."]}}]}})
Loading