Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions backend/ng/challenge/tests/test_import_challenge_from_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def test_import_minimal_challenge_success(self, test_event):
points: 10
answer: "4"
max_attempts: 3
services:
app:
image: nginx:latest
hostname: app-server
"""

# Act
Expand Down Expand Up @@ -95,6 +99,11 @@ def test_import_challenge_with_hints(self, test_event):
body: This is the second hint
preview: Second hint preview
deduction: 15

services:
app:
image: nginx:latest
hostname: app-server
"""

# Act
Expand Down Expand Up @@ -126,6 +135,10 @@ def test_import_challenge_with_tags(self, test_event):
answer: "4"
max_attempts: 3
tags: ["web", "beginner", "sql"]
services:
app:
image: nginx:latest
hostname: app-server
"""

# Act
Expand Down Expand Up @@ -158,6 +171,10 @@ def test_import_challenge_string_answers(self, test_event, answer_type,
placeholder: "flag{{...}}"
max_attempts: 3
answer: "{answer_data}"
services:
app:
image: nginx:latest
hostname: app-server
"""

# Act
Expand Down Expand Up @@ -196,6 +213,10 @@ def test_import_challenge_template_answer_with_variable(self, test_event):
placeholder: "flag{...}"
max_attempts: 3
answer: *flag_suffix
services:
app:
image: nginx:latest
hostname: app-server
"""

# Act
Expand Down
46 changes: 36 additions & 10 deletions backend/ng/challenge/tests/test_lint_challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class TestLintChallenge:
points: 10
answer: "4"
max_attempts: 3
services:
web:
image: nginx:latest
hostname: web-server
""", None),

# Test valid YAML with services but no warnings
Expand Down Expand Up @@ -92,6 +96,9 @@ def test_lint_challenge_valid_yaml_no_warnings(self, valid_yaml, expected_warnin
web:
image: nginx:latest
hostname: web-server
networks:
- undefined-net
- competitor_net
networks:
competitor_net:
internal: false
Expand Down Expand Up @@ -126,7 +133,7 @@ def test_lint_challenge_valid_yaml_with_warnings(self, yaml_with_warnings, expec
# field is optional in warnings

def test_lint_challenge_compose_file_warnings(self):
"""Test ComposeFile-level warnings: no services and no networks"""
"""Test ComposeFile-level warnings: no networks"""
yaml_content = """
x-challenge:
name: No Services Challenge
Expand All @@ -138,6 +145,10 @@ def test_lint_challenge_compose_file_warnings(self):
points: 10
answer: "4"
max_attempts: 3
services:
app:
image: nginx:latest
hostname: app-server
"""
# Act
result = lint_challenge(yaml_content)
Expand All @@ -148,7 +159,7 @@ def test_lint_challenge_compose_file_warnings(self):

# Should have warnings about no services and no networks
warning_messages = [w["message"] for w in result["warnings"]]
assert any("No services defined" in msg for msg in warning_messages)
# assert any("No services defined" in msg for msg in warning_messages)
assert any("No networks defined" in msg for msg in warning_messages)

@pytest.mark.parametrize("service_field,field_value,expected_warning", [
Expand Down Expand Up @@ -241,33 +252,39 @@ def test_lint_challenge_multiple_service_warnings(self):
for expected_warning in expected_warnings:
assert any(expected_warning in msg for msg in warning_messages), f"Expected warning '{expected_warning}' not found"

@pytest.mark.parametrize("network_config,expected_warning", [
@pytest.mark.parametrize("network_config,expected_warning,network_list", [
# Network with internal: false should warn
("""
competitor_net:
external-network:
internal: false
""", "internal field is False, this network will not be created in production"),
""", "internal field is False, this network will not be created in production",
"- competitor_net\n - external-network"),

# Network with no internal field should warn (appears as undefined)
("""
competitor_net:
missing-internal:
""", "is not defined, so is external and will not be created"),
""", "is not defined, so is external and will not be created"
, "- competitor_net\n - missing-internal"),


# Network defined as None should warn
("""
competitor_net:
undefined-network:
""", "is not defined, so is external and will not be created"),
""", "is not defined, so is external and will not be created"
, "- competitor_net\n - undefined-network"),
])
def test_lint_challenge_network_warnings(self, network_config, expected_warning):
def test_lint_challenge_network_warnings(self, network_config, expected_warning, network_list):
"""Test network-specific warnings"""
yaml_content = f"""
services:
web:
image: nginx:latest
hostname: web-server
networks:
{network_list}
networks:{network_config}
x-challenge:
name: Network Warning Test
Expand Down Expand Up @@ -297,6 +314,11 @@ def test_lint_challenge_multiple_network_warnings(self):
web:
image: nginx:latest
hostname: web-server
networks:
- external-net
- missing-internal-net
- undefined-net
- competitor_net
networks:
external-net:
internal: false
Expand Down Expand Up @@ -341,6 +363,8 @@ def test_lint_challenge_warning_field_paths(self):
image: nginx:latest
hostname: web-server
build: .
networks:
- competitor_net
api:
image: python:3.9
hostname: api-server
Expand Down Expand Up @@ -374,10 +398,8 @@ def test_lint_challenge_warning_field_paths(self):
field_paths = [w["field"] for w in warnings_with_fields]

# Look for service and network warning field paths
service_fields = [f for f in field_paths if "service warnings" in f]
network_fields = [f for f in field_paths if "network warnings" in f]
network_fields = [f for f in field_paths if "network" in f]

assert len(service_fields) > 0, "Should have service warning field paths"
assert len(network_fields) > 0, "Should have network warning field paths"

def test_lint_challenge_no_warnings_for_good_config(self):
Expand Down Expand Up @@ -525,6 +547,10 @@ def test_lint_challenge_with_template_variables(self):
points: 100
max_attempts: 3
answer: *flag_suffix
services:
web:
image: nginx:latest
hostname: web-server
"""
# Act
result = lint_challenge(yaml_content)
Expand Down
26 changes: 25 additions & 1 deletion backend/ng/challenge/tests/test_update_challenge_from_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def existing_challenge(self, db_session, test_event) -> Challenge:
# Add YAML
yaml_content = """
version: "3"
challenge:
x-challenge:
name: Original Challenge
description: Original description
"""
Expand Down Expand Up @@ -80,6 +80,10 @@ def test_update_challenge_basic_fields(self, db_session, existing_challenge: Cha
points: 10
answer: "4"
max_attempts: 3
services:
web:
image: nginx:latest
hostname: web-server
"""
# Act
result = update_challenge_from_yaml(existing_challenge, yaml_content)
Expand Down Expand Up @@ -122,6 +126,10 @@ def test_update_challenge_cannot_remove_existing_hints(self, db_session, existin
answer: "4"
max_attempts: 3
hints: []
services:
web:
image: nginx:latest
hostname: web-server
"""

with pytest.raises(ValidationError, match="Cannot remove existing hints"):
Expand Down Expand Up @@ -155,6 +163,10 @@ def test_update_challenge_cannot_remove_existing_questions(self, db_session, exi
points: 10
answer: "4"
max_attempts: 3
services:
web:
image: nginx:latest
hostname: web-server
"""

with pytest.raises(ValidationError, match="Cannot remove existing questions"):
Expand Down Expand Up @@ -183,6 +195,10 @@ def test_update_challenge_cannot_remove_existing_services(self, db_session, exis
points: 10
answer: "4"
max_attempts: 3
services:
zip:
image: nginx:latest
hostname: web-server
"""

with pytest.raises(ValidationError, match="Cannot remove existing services"):
Expand Down Expand Up @@ -221,6 +237,10 @@ def test_update_challenge_tag_management(self, db_session, existing_challenge: C
answer: "4"
max_attempts: 3
tags: {tags_yaml}
services:
web:
image: nginx:latest
hostname: web-server
"""

# Act
Expand Down Expand Up @@ -293,6 +313,10 @@ def test_update_challenge_with_variables_and_template_answers(self, db_session,
points: 100
max_attempts: 5
answer: *flag_value
services:
web:
image: nginx:latest
hostname: web-server
"""

# Act
Expand Down
2 changes: 1 addition & 1 deletion backend/ng/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pre-commit==4.2.0
ruff==0.12.0
attrs==25.3.0
cattrs==25.2.0
cyber-skyline-chall-parser==0.5.0
cyber-skyline-chall-parser==0.5.1
faker>=37.4.0
pyyaml>=6.0.2
Flask-SocketIO==5.5.1
Expand Down
Loading