Skip to content

Commit 9366f01

Browse files
committed
Add testing directory
1 parent cb9fe15 commit 9366f01

File tree

5 files changed

+261
-0
lines changed

5 files changed

+261
-0
lines changed

infrahub_sdk/testing/__init__.py

Whitespace-only changes.

infrahub_sdk/testing/docker.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import pytest
2+
from infrahub_testing.helpers import TestInfrahubDocker
3+
4+
from .. import Config, InfrahubClient, InfrahubClientSync
5+
6+
7+
class TestInfrahubDockerClient(TestInfrahubDocker):
8+
@pytest.fixture(scope="class")
9+
def infrahub_client(self, infrahub_port: int) -> InfrahubClient:
10+
return InfrahubClient(config=Config(address=f"http://localhost:{infrahub_port}"))
11+
12+
@pytest.fixture(scope="class")
13+
def infrahub_client_sync(self, infrahub_port: int) -> InfrahubClientSync:
14+
return InfrahubClientSync(config=Config(address=f"http://localhost:{infrahub_port}"))

infrahub_sdk/testing/repository.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import asyncio
2+
import shutil
3+
from dataclasses import dataclass, field
4+
from enum import Enum
5+
from pathlib import Path
6+
from typing import Optional
7+
8+
from git.repo import Repo
9+
10+
from infrahub_sdk import InfrahubClient
11+
from infrahub_sdk.graphql import Mutation
12+
from infrahub_sdk.protocols import CoreGenericRepository
13+
14+
15+
# NOTE we shouldn't duplicate this, need to figure out a better solution
16+
class RepositorySyncStatus(str, Enum):
17+
UNKNOWN = "unknown"
18+
IN_SYNC = "in-sync"
19+
ERROR_IMPORT = "error-import"
20+
SYNCING = "syncing"
21+
22+
23+
class GitRepoType(str, Enum):
24+
INTEGRATED = "CoreRepository"
25+
READ_ONLY = "CoreReadOnlyRepository"
26+
27+
28+
@dataclass
29+
class GitRepo:
30+
name: str
31+
src_directory: Path
32+
dst_directory: Path
33+
34+
type: GitRepoType = GitRepoType.INTEGRATED
35+
36+
_repo: Optional[Repo] = None
37+
initial_branch: str = "main"
38+
directories_to_ignore: list[str] = field(default_factory=list)
39+
remote_directory_name: str = "/remote"
40+
_branches: list[str] = field(default_factory=list)
41+
42+
@property
43+
def repo(self) -> Repo:
44+
if self._repo:
45+
return self._repo
46+
raise ValueError("Repo hasn't been initialized yet")
47+
48+
def __post_init__(self) -> None:
49+
self.init()
50+
51+
@property
52+
def path(self) -> str:
53+
return str(self.src_directory / self.name)
54+
55+
def init(self) -> None:
56+
shutil.copytree(
57+
src=self.src_directory,
58+
dst=self.dst_directory / self.name,
59+
ignore=shutil.ignore_patterns(".git"),
60+
)
61+
self._repo = Repo.init(self.dst_directory / self.name, initial_branch=self.initial_branch)
62+
for untracked in self.repo.untracked_files:
63+
self.repo.index.add(untracked)
64+
self.repo.index.commit("First commit")
65+
66+
self.repo.git.checkout(self.initial_branch)
67+
68+
async def add_to_infrahub(self, client: InfrahubClient, branch: str | None = None) -> dict:
69+
input_data = {
70+
"data": {
71+
"name": {"value": self.name},
72+
"location": {"value": f"{self.remote_directory_name}/{self.name}"},
73+
},
74+
}
75+
76+
query = Mutation(
77+
mutation=f"{self.type.value}Create",
78+
input_data=input_data,
79+
query={"ok": None},
80+
)
81+
82+
return await client.execute_graphql(
83+
query=query.render(), branch_name=branch or self.initial_branch, tracker="mutation-repository-create"
84+
)
85+
86+
async def wait_for_sync_to_complete(
87+
self, client: InfrahubClient, branch: str | None = None, interval: int = 5, retries: int = 6
88+
) -> bool:
89+
for _ in range(retries):
90+
repo = await client.get(
91+
kind=CoreGenericRepository, # type: ignore[type-abstract]
92+
name__value=self.name,
93+
branch=branch or self.initial_branch,
94+
)
95+
status = repo.sync_status.value
96+
if status == RepositorySyncStatus.IN_SYNC.value:
97+
return True
98+
if status == RepositorySyncStatus.ERROR_IMPORT.value:
99+
return False
100+
101+
await asyncio.sleep(interval)
102+
103+
return False

infrahub_sdk/testing/schemas/__init__.py

Whitespace-only changes.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
from typing import Any
2+
3+
import pytest
4+
5+
from infrahub_sdk import InfrahubClient
6+
from infrahub_sdk.node import InfrahubNode
7+
8+
NAMESPACE = "Testing"
9+
10+
TESTING_MANUFACTURER = f"{NAMESPACE}Manufacturer"
11+
TESTING_PERSON = f"{NAMESPACE}Person"
12+
TESTING_CAR = f"{NAMESPACE}Car"
13+
14+
15+
class SchemaCarPerson:
16+
@pytest.fixture(scope="class")
17+
def schema_person_base(self) -> dict[str, Any]:
18+
return {
19+
"name": "Person",
20+
"namespace": NAMESPACE,
21+
"include_in_menu": True,
22+
"label": "Person",
23+
"human_friendly_id": ["name__value"],
24+
"attributes": [
25+
{"name": "name", "kind": "Text", "unique": True},
26+
{"name": "description", "kind": "Text", "optional": True},
27+
{"name": "height", "kind": "Number", "optional": True},
28+
{"name": "age", "kind": "Number", "optional": True},
29+
],
30+
"relationships": [
31+
{"name": "cars", "kind": "Generic", "optional": True, "peer": TESTING_CAR, "cardinality": "many"}
32+
],
33+
}
34+
35+
@pytest.fixture(scope="class")
36+
def schema_car_base(self) -> dict[str, Any]:
37+
return {
38+
"name": "Car",
39+
"namespace": NAMESPACE,
40+
"include_in_menu": True,
41+
"default_filter": "name__value",
42+
"human_friendly_id": ["owner__name__value", "name__value"],
43+
"label": "Car",
44+
"attributes": [
45+
{"name": "name", "kind": "Text"},
46+
{"name": "description", "kind": "Text", "optional": True},
47+
{"name": "color", "kind": "Text"},
48+
],
49+
"relationships": [
50+
{
51+
"name": "owner",
52+
"kind": "Attribute",
53+
"optional": False,
54+
"peer": TESTING_PERSON,
55+
"cardinality": "one",
56+
},
57+
{
58+
"name": "manufacturer",
59+
"kind": "Attribute",
60+
"optional": False,
61+
"peer": TESTING_MANUFACTURER,
62+
"cardinality": "one",
63+
"identifier": "car__manufacturer",
64+
},
65+
],
66+
}
67+
68+
@pytest.fixture(scope="class")
69+
def schema_manufacturer_base(self) -> dict[str, Any]:
70+
return {
71+
"name": "Manufacturer",
72+
"namespace": NAMESPACE,
73+
"include_in_menu": True,
74+
"label": "Manufacturer",
75+
"human_friendly_id": ["name__value"],
76+
"attributes": [{"name": "name", "kind": "Text"}, {"name": "description", "kind": "Text", "optional": True}],
77+
"relationships": [
78+
{
79+
"name": "cars",
80+
"kind": "Generic",
81+
"optional": True,
82+
"peer": TESTING_CAR,
83+
"cardinality": "many",
84+
"identifier": "car__manufacturer",
85+
},
86+
{
87+
"name": "customers",
88+
"kind": "Generic",
89+
"optional": True,
90+
"peer": TESTING_PERSON,
91+
"cardinality": "many",
92+
"identifier": "person__manufacturer",
93+
},
94+
],
95+
}
96+
97+
@pytest.fixture(scope="class")
98+
def schema_base(
99+
self,
100+
schema_car_base: dict[str, Any],
101+
schema_person_base: dict[str, Any],
102+
schema_manufacturer_base: dict[str, Any],
103+
) -> dict[str, Any]:
104+
return {
105+
"version": "1.0",
106+
"nodes": [schema_person_base, schema_car_base, schema_manufacturer_base],
107+
}
108+
109+
async def create_persons(self, client: InfrahubClient, branch: str) -> list[InfrahubNode]:
110+
john = await client.create(kind=TESTING_PERSON, name="John Doe", branch=branch)
111+
await john.save()
112+
113+
jane = await client.create(kind=TESTING_PERSON, name="Jane Doe", branch=branch)
114+
await jane.save()
115+
116+
return [john, jane]
117+
118+
async def create_manufacturers(self, client: InfrahubClient, branch: str) -> list[InfrahubNode]:
119+
obj1 = await client.create(kind=TESTING_MANUFACTURER, name="Volkswagen", branch=branch)
120+
await obj1.save()
121+
122+
obj2 = await client.create(kind=TESTING_MANUFACTURER, name="Renault", branch=branch)
123+
await obj2.save()
124+
125+
obj3 = await client.create(kind=TESTING_MANUFACTURER, name="Mercedes", branch=branch)
126+
await obj3.save()
127+
128+
return [obj1, obj2, obj3]
129+
130+
async def create_initial_data(self, client: InfrahubClient, branch: str) -> dict[str, list[InfrahubNode]]:
131+
persons = await self.create_persons(client=client, branch=branch)
132+
manufacturers = await self.create_manufacturers(client=client, branch=branch)
133+
134+
car10 = await client.create(
135+
kind=TESTING_CAR, name="Golf", color="Black", manufacturer=manufacturers[0].id, owner=persons[0].id
136+
)
137+
await car10.save()
138+
139+
car20 = await client.create(
140+
kind=TESTING_CAR, name="Megane", color="Red", manufacturer=manufacturers[1].id, owner=persons[1].id
141+
)
142+
await car20.save()
143+
144+
return {TESTING_PERSON: persons, TESTING_CAR: [car10, car20], TESTING_MANUFACTURER: manufacturers}

0 commit comments

Comments
 (0)