Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
efd483e
feat: Upgrade schemathesis
afonsobspinto Oct 23, 2025
8b82aa5
chore: Simplify schemathesis tests on workflows and common apps
afonsobspinto Oct 23, 2025
b8c65c2
chore: Update parametrize parameters
afonsobspinto Oct 23, 2025
6d3d2fd
chore: Remove deprecated __dict__ print
afonsobspinto Oct 23, 2025
4e29a21
chore: Update parametrize parameters
afonsobspinto Oct 23, 2025
783d59d
chore: Update codefresh assert
afonsobspinto Oct 23, 2025
2479095
chore: Update test codefresh pipeline
afonsobspinto Oct 24, 2025
89c8425
chore: Update runParams
afonsobspinto Oct 24, 2025
a0f03d2
chore: Update testing docs
afonsobspinto Oct 24, 2025
b95378f
chore: Prevent trace requests on st tests
afonsobspinto Oct 24, 2025
70346b4
fix: Update samples api
afonsobspinto Oct 27, 2025
93bff9a
fix: Update workflows api
afonsobspinto Oct 27, 2025
f179fa7
fix: Update volumemanager api
afonsobspinto Oct 27, 2025
d5ae87e
fix: Update common api
afonsobspinto Oct 27, 2025
fcb2ae9
fix: Update common api
afonsobspinto Oct 27, 2025
2152d74
fix: Update volumemanager api
afonsobspinto Oct 27, 2025
82de3bc
fix: Update workflows api
afonsobspinto Oct 27, 2025
383a25f
fix: Update samples api
afonsobspinto Oct 27, 2025
61314a2
fix: Update volumemanager examples
afonsobspinto Oct 27, 2025
4c028de
fix: Update common api
afonsobspinto Oct 27, 2025
e6fb26f
fix: Ignore problematic workflow tests
afonsobspinto Oct 27, 2025
4f42299
fix: Ignore problematic sample operations
afonsobspinto Oct 27, 2025
ff727d5
chore: Correct schemathesis toml syntax
afonsobspinto Oct 27, 2025
c3048e4
fix: Update volumemanager api
afonsobspinto Oct 27, 2025
7ca0725
fix: Suppress filter_too_much check on common
afonsobspinto Oct 27, 2025
bdeefb8
chore: Correct schemathesis toml syntax
afonsobspinto Oct 27, 2025
36049d1
fix: Update volumemanager api
afonsobspinto Oct 27, 2025
bdcfd45
chore: Move schemathesis config to run param
afonsobspinto Oct 27, 2025
77dab73
chore: Correct schemathesis toml syntax
afonsobspinto Oct 27, 2025
a7d206f
chore: Update codefresh test pipeline
afonsobspinto Oct 27, 2025
1ae316b
fix: Update volume manager api
afonsobspinto Oct 27, 2025
1f5ae79
fix: Update volume manager api
afonsobspinto Oct 27, 2025
45146da
fix: Update st check imports
afonsobspinto Oct 27, 2025
3c52e6f
chore: Update st configurations
afonsobspinto Oct 27, 2025
9805990
chore: Remove unused imports
afonsobspinto Oct 27, 2025
a511aae
fix: Add validation to volumeManager params
afonsobspinto Oct 27, 2025
1dcefc3
fix: Set emtpy string previous_search_token to None
afonsobspinto Oct 27, 2025
4e261e2
fix: Update from_uri calls
afonsobspinto Oct 27, 2025
044ec68
fix: Update volume manager api
afonsobspinto Oct 27, 2025
b10f977
chore: Update schemathesis config
afonsobspinto Oct 27, 2025
c6ac8bd
chore: Update runParams
afonsobspinto Oct 27, 2025
da0533f
chore: Add debug messages
afonsobspinto Oct 27, 2025
a63cfef
chore: Override size parameter
afonsobspinto Oct 27, 2025
632f8dd
fix: Update samples test
afonsobspinto Oct 27, 2025
0f0ccd1
chore: Limit volumemanager tests to examples only
afonsobspinto Oct 27, 2025
92f93db
chore: Update codefresh pipeline
afonsobspinto Oct 28, 2025
5c5b2d2
chore: Update schemathesis config
afonsobspinto Oct 28, 2025
d095a3d
feat(api tests): switch schemathesis invocation to harness-test; move…
afonsobspinto Oct 28, 2025
b7c03db
refactor(api tests): simplify _get_auth_headers to only USERNAME/PASS…
afonsobspinto Oct 28, 2025
aca9b79
chore: Update codefresh spec
afonsobspinto Oct 28, 2025
2a6d132
fix: Add auth hooks to custom tests
afonsobspinto Oct 28, 2025
6f49096
fix: Update test api docker image
afonsobspinto Oct 28, 2025
7386779
fix: Update dockerignore
afonsobspinto Oct 28, 2025
7df59dc
fix: Update test api docker image
afonsobspinto Oct 28, 2025
be05ea3
fix: Set CH_VALUES_PATH location
afonsobspinto Oct 30, 2025
a674913
Merge branch 'develop' into feature/bump_schemathesis
afonsobspinto Oct 30, 2025
52b6c0f
fix: Update harness-test working dir
afonsobspinto Oct 30, 2025
9feb464
fix: Move get_token to local import
afonsobspinto Nov 2, 2025
0ccb92d
fix: Adapt tests to match with new api testing strategy
afonsobspinto Nov 2, 2025
b2bc66a
feat: Connect -c flag of harness-test
afonsobspinto Nov 2, 2025
8ba18e4
chore: Update codefresh specification
afonsobspinto Nov 2, 2025
35b4221
fix: Correct helm chart location
afonsobspinto Nov 2, 2025
16d1290
style: Run linter
afonsobspinto Nov 2, 2025
ea3e32d
fix: Correct harness-test ch location
afonsobspinto Nov 2, 2025
cfb4191
fix: Update schemathesis hooks signature
afonsobspinto Nov 2, 2025
30c894e
feat: Use first root path as default helm chart root folder
afonsobspinto Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ docs
/infrastructure
/blueprint
test
/tools/deployment-cli-tools
.github
.git
.vscode
Expand Down
11 changes: 4 additions & 7 deletions application-templates/base/test/api/test_st.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import os
from pprint import pprint
import schemathesis as st
from schemathesis.checks import response_schema_conformance, not_a_server_error

from cloudharness_test import apitest_init # include to perform default authorization
from cloudharness_test import apitest_init # include to register default hooks
from cloudharness_test import apitest_auth_hooks # include to register authentication hooks

app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")

schema = st.from_uri(app_url + "/openapi.json")
schema = st.openapi.from_url(app_url + "/openapi.json")


@schema.parametrize(endpoint="/ping")
@schema.include(path="/ping", method="GET").parametrize()
def test_ping(case):
response = case.call()
pprint(response.__dict__)
assert response.status_code == 200, "this api errors on purpose"
13 changes: 5 additions & 8 deletions application-templates/django-base/api/test_st.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import os
from pprint import pprint
import schemathesis as st
from schemathesis.checks import response_schema_conformance, not_a_server_error

from cloudharness_test import apitest_init # include to perform default authorization
from cloudharness_test import apitest_init # include to register default hooks
from cloudharness_test import apitest_auth_hooks # include to register authentication hooks

app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")

try:
schema = st.from_uri(app_url + "/openapi.json")
schema = st.openapi.from_url(app_url + "/openapi.json")
except:
# support alternative schema location
schema = st.from_uri(app_url.replace("/api", "") + "/openapi.json")
schema = st.openapi.from_url(app_url.replace("/api", "") + "/openapi.json")


@schema.parametrize(endpoint="/ping")
@schema.include(path="/ping", method="GET").parametrize()
def test_ping(case):
response = case.call()
pprint(response.__dict__)
assert response.status_code == 200, "this api errors on purpose"


Expand Down
5 changes: 4 additions & 1 deletion applications/common/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ paths:
description: Sentry not configured for the given application
'404':
content:
application/json:
schema:
type: object
application/problem+json:
schema:
type: object
text/html:
schema:
type: string
description: Sentry not configured for the given application
description: Application not found
operationId: getdsn
summary: Gets the Sentry DSN for a given application
description: Gets the Sentry DSN for a given application
Expand Down
4 changes: 3 additions & 1 deletion applications/common/deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ harness:
enabled: true
autotest: true
checks:
- all
- all
runParams:
- "--suppress-health-check=filter_too_much"
2 changes: 2 additions & 0 deletions applications/common/schemathesis.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[phases.coverage]
unexpected-methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def getdsn(appname): # noqa: E501
try:
ch_app = applications.get_configuration(appname)
except applications.ConfigurationCallException as e:
return {"error": f"Application `{appname}` does not exist"}, 400
return {"error": f"Application `{appname}` does not exist"}, 404
if ch_app.is_sentry_enabled():
if global_dsn:
# if a global dsn env var is set and not empty then use this
Expand Down
5 changes: 4 additions & 1 deletion applications/common/server/common/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@ paths:
description: Sentry not configured for the given application
"404":
content:
application/json:
schema:
type: object
application/problem+json:
schema:
type: object
text/html:
schema:
type: string
description: Sentry not configured for the given application
description: Application not found
summary: Gets the Sentry DSN for a given application
tags:
- Sentry
Expand Down
4 changes: 4 additions & 0 deletions applications/samples/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ paths:
schema:
type: string
description: Check if token is valid
"400":
description: Bad request
"401":
description: "invalid token, unauthorized"
security:
Expand All @@ -74,6 +76,8 @@ paths:
schema:
type: string
description: Check if token is valid
"400":
description: Bad request
"401":
description: "invalid token, unauthorized"
security:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def valid_token(): # noqa: E501
"""
from cloudharness.middleware import get_authentication_token
token = get_authentication_token()
if not token:
return 'Unauthorized', 401
return 'OK!'


Expand All @@ -29,5 +31,7 @@ def valid_cookie(): # noqa: E501
from cloudharness.middleware import get_authentication_token
from cloudharness.auth import decode_token
token = get_authentication_token()
if not token:
return 'Unauthorized', 401
assert decode_token(token)
return 'OK'
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def delete_sample_resource(sampleresource_id): # noqa: E501
except resource_service.ResourceNotFound:
return "Resource not found", 404
except ValueError:
return "sampleresource_id must be integer", 400
return "Resource not found", 404
return 'OK', 204


Expand All @@ -64,7 +64,7 @@ def get_sample_resource(sampleresource_id): # noqa: E501
except resource_service.ResourceNotFound:
return "Resource not found", 404
except ValueError:
return "sampleresource_id must be integer", 400
return "Resource not found", 404


def get_sample_resources(): # noqa: E501
Expand Down Expand Up @@ -101,4 +101,4 @@ def update_sample_resource(sampleresource_id, sample_resource=None): # noqa: E5
except resource_service.ResourceNotFound:
return "Resource not found", 404
except ValueError:
return "sampleresource_id must be integer", 400
return "Resource not found", 404
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ def info_from_bearerAuth(token):
:return: Decoded token information or None if token is invalid
:rtype: dict | None
"""
if token is None:
return None
return {'uid': 'user_id'}
11 changes: 5 additions & 6 deletions applications/samples/deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,15 @@ harness:
checks:
- all
runParams:
- "--skip-deprecated-operations"
- "--exclude-deprecated"
- "--exclude-operation-id=submit_sync"
- "--exclude-operation-id=submit_sync_with_results"
- "--exclude-operation-id=error"
- "--hypothesis-suppress-health-check=too_slow"
- "--hypothesis-deadline=180000"
- "--suppress-health-check=too_slow"
- "--suppress-health-check=filter_too_much"
- "--request-timeout=180000"
- "--hypothesis-max-examples=2"
- "--show-trace"
- "--exclude-checks=ignored_auth" # ignored_auth is not working on schemathesis 3.36.0
- "--max-examples=2"
- "--exclude-checks=ignored_auth"

dockerfile:
buildArgs:
Expand Down
12 changes: 12 additions & 0 deletions applications/samples/schemathesis.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[phases.coverage]
unexpected-methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]

[[operations]]
include-operation-id = "valid_token"
[operations.checks]
negative_data_rejection.enabled = false

[[operations]]
include-operation-id = "valid_cookie"
[operations.checks]
negative_data_rejection.enabled = false
28 changes: 18 additions & 10 deletions applications/samples/test/api/test_st.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
import os
from pprint import pprint
import schemathesis as st
from schemathesis.checks import response_schema_conformance, not_a_server_error
from schemathesis.specs.openapi.checks import response_schema_conformance

from cloudharness_test import apitest_init # include to perform default authorization
from cloudharness_test import apitest_init # include to register default hooks
from cloudharness_test import apitest_auth_hooks # include to register authentication hooks

app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")

schema = st.from_uri(app_url + "/openapi.json")
schema = st.openapi.from_url(app_url + "/openapi.json")


@schema.parametrize(endpoint="/error")
@schema.include(path="/error", method="GET").parametrize()
def test_api(case):
response = case.call()
pprint(response.__dict__)
assert response.status_code >= 500, "this api errors on purpose"

if case.method == "GET":
# Assert that this endpoint returns a 500 error as expected
assert response.status_code == 500, f"Expected 500 error, got {response.status_code}. This api errors on purpose."
elif case.method == "OPTIONS":
# OPTIONS requests typically return 200 OK
assert response.status_code == 200, f"Expected 200 OK for OPTIONS, got {response.status_code}."
else:
# Other methods should return 405 (Method Not Allowed)
assert response.status_code == 405, f"Expected 405 (Method Not Allowed) for {case.method}, got {response.status_code}."

@schema.parametrize(endpoint="/valid")

@schema.include(path="/valid", method="GET").parametrize()
def test_bearer(case):
response = case.call()

case.validate_response(response, checks=(response_schema_conformance,))


@schema.parametrize(endpoint="/valid-cookie")
@schema.include(path="/valid-cookie", method="GET").parametrize()
def test_cookie(case):
response = case.call()
case.validate_response(response, checks=(response_schema_conformance,))


@schema.parametrize(endpoint="/sampleresources", method="POST")
@schema.include(path="/sampleresources", method="POST").parametrize()
def test_response(case):
response = case.call()
case.validate_response(response, checks=(response_schema_conformance,))
32 changes: 26 additions & 6 deletions applications/volumemanager/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/PersistentVolumeClaim'
type: string
example: "Saved!"
description: Save successful.
"400":
description: The Persistent Volume Claim already exists.
description: Bad request - invalid or missing required fields.
"401":
description: Unauthorized - No authorization token provided.
"500":
description: Internal server error.
security:
- bearerAuth: []
summary: Create a Persistent Volume Claim in Kubernetes
Expand All @@ -49,6 +54,9 @@ paths:
required: true
schema:
type: string
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'
minLength: 1
maxLength: 253
style: simple
responses:
"200":
Expand All @@ -57,6 +65,10 @@ paths:
schema:
$ref: '#/components/schemas/PersistentVolumeClaim'
description: The Persistent Volume Claim.
"400":
description: Bad request - invalid path parameter or headers.
"401":
description: Unauthorized - No authorization token provided.
"404":
description: The Persistent Volume Claim was not found.
security:
Expand All @@ -69,21 +81,29 @@ components:
schemas:
PersistentVolumeClaimCreate:
example:
size: 2Gi (see also https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/resources.md#resource-quantities)
size: 2Gi
name: pvc-1
properties:
name:
description: Unique name for the Persisten Volume Claim to create.
example: pvc-1
type: string
minLength: 1
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'
size:
description: The size of the Persistent Volume Claim to create.
example: 2Gi (see also https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/resources.md#resource-quantities)
example: 2Gi
type: string
minLength: 1
pattern: '^[1-9][0-9]*(Ei|Pi|Ti|Gi|Mi|Ki|E|P|T|G|M|K)?$'
required:
- name
- size
type: object
additionalProperties: false
PersistentVolumeClaim:
example:
size: 2Gi (see also https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/resources.md#resource-quantities)
size: 2Gi
name: pvc-1
namespace: ch
accessmode: ReadWriteMany
Expand All @@ -102,7 +122,7 @@ components:
type: string
size:
description: The size of the Persistent Volume Claim.
example: 2Gi (see also https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/resources.md#resource-quantities)
example: 2Gi
type: string
type: object
securitySchemes:
Expand Down
5 changes: 4 additions & 1 deletion applications/volumemanager/deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ harness:
enabled: true
autotest: true
checks:
- all
- all
runParams:
- "--phases=examples"
- "--max-examples=1"
5 changes: 5 additions & 0 deletions applications/volumemanager/schemathesis.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[phases.coverage]
unexpected-methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]

[checks]
negative_data_rejection.enabled = false
Loading
Loading