Skip to content

Commit 9bccdba

Browse files
author
Patrick J. McNerthney
committed
More pytest cases and add CI report
1 parent 58e7bcb commit 9bccdba

File tree

7 files changed

+180
-21
lines changed

7 files changed

+180
-21
lines changed

.github/workflows/ci.yaml

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name: CI
33
on:
44
push:
55
branches:
6-
- main
7-
- release-*
6+
- main
7+
- release-*
88
pull_request: {}
99
workflow_dispatch:
1010
inputs:
@@ -53,22 +53,38 @@ jobs:
5353
# - name: Lint
5454
# run: hatch run lint:check
5555

56-
unit-test:
56+
test:
5757
runs-on: ubuntu-24.04
5858
steps:
59-
- name: Checkout
60-
uses: actions/checkout@v4
61-
62-
- name: Setup Python
63-
uses: actions/setup-python@v5
64-
with:
65-
python-version: ${{ env.PYTHON_VERSION }}
66-
67-
- name: Setup Hatch
68-
run: pipx install hatch==1.14.1
69-
70-
- name: Run Unit Tests
71-
run: hatch run test:unit
59+
- name: Checkout
60+
uses: actions/checkout@v4
61+
62+
- name: Setup Python
63+
uses: actions/setup-python@v5
64+
with:
65+
python-version: ${{ env.PYTHON_VERSION }}
66+
67+
- name: Setup Hatch
68+
run: pipx install hatch==1.14.1
69+
70+
- name: Run Unit Tests
71+
run: hatch run test:ci
72+
73+
- name: Pytest coverage comment
74+
uses: MishaKav/pytest-coverage-comment@main
75+
with:
76+
title: Function Pythonic Title
77+
junitxml-title: Function Pythonic Junit Title
78+
badge-title: Function Pythonic Badge Title
79+
junitxml-path: reports/pytest-junit.xml
80+
pytest-xml-coverage-path: reports/pytest-coverage.xml
81+
#hide-badge: false
82+
#hide-report: false
83+
#create-new-comment: false
84+
#hide-comment: false
85+
#report-only-changed-files: false
86+
#remove-link-from-badge: false
87+
#unique-id-for-comment: python3.8
7288

7389
# We want to build most packages for the amd64 and arm64 architectures. To
7490
# speed this up we build single-platform packages in parallel. We then upload

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ coverage.xml
5353
.hypothesis/
5454
.pytest_cache/
5555
cover/
56+
reports/
5657

5758
# Translations
5859
*.mo

examples/helm-copy-secret/composition.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ spec:
4141
#vcluster_secrets = self.requireds.Secret('v1', 'Secret', namespace, secret_name)
4242
vcluster_secrets = self.requireds.Secret('v1', 'Secret', labels={'vcluster-name':name})
4343
for secret in vcluster_secrets:
44-
if secret.metadata.name != secret_name:
44+
if secret.metadata.namespace != namespace or secret.metadata.name != secret_name:
4545
continue
4646
argocd_secret = self.resources.secret('v1', 'Secret', 'argocd', secret_name)
4747
argocd_secret.metadata.labels['argocd.argoproj.io/secret-type'] = 'cluster'

examples/helm-copy-secret/xrd.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: CompositeResourceDefinition
3+
metadata:
4+
name: xclusters.example.joebowbeer.com
5+
spec:
6+
group: example.joebowbeer.com
7+
names:
8+
kind: XCluster
9+
plural: xclusters
10+
scope: Cluster
11+
versions:
12+
- name: v1alpha1
13+
served: true
14+
referenceable: true
15+
schema:
16+
openAPIV3Schema:
17+
type: object
18+
properties:
19+
spec:
20+
type: object
21+
properties: {}

pyproject.toml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,29 @@ validate-bump = false # Allow going from 0.0.0.dev0+x to 0.0.0.dev0+y
4343
type = "virtual"
4444
path = ".venv-default"
4545
dependencies = ["ipython==9.1.0"]
46-
scripts = { development = "python function/main.py --insecure --debug" }
46+
[tool.hatch.envs.default.scripts]
47+
development = "python function/main.py --insecure --debug"
4748

4849
[tool.hatch.envs.lint]
4950
type = "virtual"
5051
detached = true
5152
path = ".venv-lint"
5253
dependencies = ["ruff==0.11.5"]
53-
scripts = { check = "ruff check function tests && ruff format --diff function tests" }
54+
[tool.hatch.envs.lint.scripts]
55+
check = "ruff check function tests && ruff format --diff function tests"
5456

5557
[tool.hatch.envs.test]
5658
type = "virtual"
5759
path = ".venv-test"
58-
dependencies = ["pytest==8.4.1", "pytest-asyncio==1.1.0"]
59-
scripts = { unit = "python -m pytest tests" }
60+
dependencies = [
61+
"pytest==8.4.1",
62+
"pytest-asyncio==1.1.0",
63+
"pytest-cov==6.2.1",
64+
]
65+
[tool.hatch.envs.test.scripts]
66+
run = "pytest tests"
67+
verbose = "pytest tests --verbose --verbose"
68+
ci = "pytest tests --junitxml=reports/pytest-junit.xml --cov --cov-report xml:reports/pytest-coverage.xml"
6069

6170
[tool.ruff]
6271
target-version = "py311"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
request:
2+
observed:
3+
composite:
4+
resource:
5+
metadata:
6+
name: my-app
7+
spec:
8+
image: nginx
9+
resources:
10+
deployment:
11+
resource:
12+
status:
13+
availableReplicas: 2
14+
conditions:
15+
- type: Available
16+
status: 'True'
17+
reason: MinimumReplicasAvailable
18+
message: Deployment has minimum availability
19+
service:
20+
resource:
21+
spec:
22+
clusterIP: 10.96.196.65
23+
input:
24+
composite: |
25+
class Composite(BaseComposite):
26+
def compose(self):
27+
labels = {'example.crossplane.io/app': self.metadata.name}
28+
29+
d = self.resources.deployment('apps/v1', 'Deployment')
30+
d.metadata.labels = labels
31+
d.spec.replicas = 2
32+
d.spec.selector.matchLabels = labels
33+
d.spec.template.metadata.labels = labels
34+
d.spec.template.spec.containers[0].name = 'app'
35+
d.spec.template.spec.containers[0].image = self.spec.image
36+
d.spec.template.spec.containers[0].ports[0].containerPort = 80
37+
d.ready = d.conditions.Available.status
38+
39+
s = self.resources.service('v1', 'Service')
40+
s.metadata.labels = labels
41+
s.spec.selector = labels
42+
s.spec.ports[0].protocol = 'TCP'
43+
s.spec.ports[0].port = 8080
44+
s.spec.ports[0].targetPort = 80
45+
s.ready = s.observed.spec.clusterIP
46+
47+
self.status.replicas = d.status.availableReplicas
48+
self.status.address = s.observed.spec.clusterIP
49+
50+
response:
51+
desired:
52+
composite:
53+
resource:
54+
status:
55+
replicas: 2
56+
address: 10.96.196.65
57+
resources:
58+
deployment:
59+
resource:
60+
apiVersion: apps/v1
61+
kind: Deployment
62+
metadata:
63+
labels:
64+
example.crossplane.io/app: my-app
65+
spec:
66+
replicas: 2
67+
selector:
68+
matchLabels:
69+
example.crossplane.io/app: my-app
70+
template:
71+
metadata:
72+
labels:
73+
example.crossplane.io/app: my-app
74+
spec:
75+
containers:
76+
- image: nginx
77+
name: app
78+
ports:
79+
- containerPort: 80
80+
ready: 1
81+
service:
82+
resource:
83+
apiVersion: v1
84+
kind: Service
85+
metadata:
86+
labels:
87+
example.crossplane.io/app: my-app
88+
spec:
89+
selector:
90+
example.crossplane.io/app: my-app
91+
ports:
92+
- protocol: TCP
93+
port: 8080
94+
targetPort: 80
95+
ready: 1

tests/utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ def message_dict(message):
154154
value = message_list_list(field, value)
155155
else:
156156
value = message_dict(value)
157+
elif field.type in (field.TYPE_DOUBLE, field.TYPE_FLOAT):
158+
if value.is_integer():
159+
value = int(value)
160+
elif field.type == field.TYPE_BYTES:
161+
value = value.decode()
157162
result[field.name] = value
158163
return result
159164

@@ -173,6 +178,9 @@ def message_map_dict(descriptor, message):
173178
value = message_list_list(value)
174179
else:
175180
value = message_dict(value)
181+
elif descriptor.type in (descriptor.TYPE_DOUBLE, descriptor.TYPE_FLOAT):
182+
if value.is_integer():
183+
value = int(value)
176184
elif descriptor.type == descriptor.TYPE_BYTES:
177185
value = value.decode()
178186
result[field] = value
@@ -193,6 +201,9 @@ def message_list_list(descriptor, message):
193201
value = message_list_list(value)
194202
else:
195203
value = message_dict(value)
204+
elif descriptor.type in (descriptor.TYPE_DOUBLE, descriptor.TYPE_FLOAT):
205+
if value.is_integer():
206+
value = int(value)
196207
elif descriptor.type == descriptor.TYPE_BYTES:
197208
value = value.decode()
198209
result.append(value)
@@ -205,6 +216,9 @@ def map_dict(message):
205216
value = map_dict(value)
206217
elif isinstance(value, ListValue):
207218
value = list_list(value)
219+
elif isinstance(value, float):
220+
if value.is_integer():
221+
value = int(value)
208222
result[field] = value
209223
return result
210224

@@ -215,5 +229,8 @@ def list_list(message):
215229
value = map_dict(value)
216230
elif isinstance(value, ListValue):
217231
value = list_list(value)
232+
elif isinstance(value, float):
233+
if value.is_integer():
234+
value = int(value)
218235
result.append(value)
219236
return result

0 commit comments

Comments
 (0)