Introduction
A repair can be accidentally undone in a later change. A security regression test turns an important requirement into a repeatable request and an expected result.
You will extend a small Python template for a protected DeskLite endpoint. Then you will check the tests themselves against two deliberately faulty local services. No test framework installation or previous test-writing experience is needed.
Exercise the Protected Endpoint
In this step, you will identify the behavior that the tests must protect.
The prepared correct service is on local port 8080. Its /tickets endpoint returns the original ticket for a valid Token and 401 with an error object for a missing or wrong Token. Authentication and the business response are separate requirements: status 200 alone does not prove the right ticket was returned.
cd /home/labex/project/ticket-tests
This lab's visible course_test_fixture_token is a public, deliberately fake test fixture. It is accepted only by these isolated teaching services and has no external authority. It is not a replacement for the private application credentials taught earlier.
Use the same curl request-header option -H as in the Token lab:
curl -i -H 'Authorization: Bearer course_test_fixture_token' http://127.0.0.1:8080/tickets
Expect HTTP 200 and {"tickets": [{"id": 1, "title": "Printer offline"}]}. -i includes the HTTP response headers, including its status.
Now omit the header entirely:
curl -i http://127.0.0.1:8080/tickets
Expect HTTP 401 and {"error": "unauthorized"}. That rejection is the required behavior, not a broken exercise.
Read the Small Test Template
In this step, you will connect each line of a test to one part of a real request.
cat test_security.py
The prepared file contains:
from api import request
def test_valid_token():
response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
assert response["status"] == 200
assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}
from api import request loads a prepared request helper. def test_valid_token(): defines a named test function. The indented lines belong to that function, just as in the lookup function from the previous lab.
request(path, headers) sends a real HTTP request and returns a Python dictionary: a mapping of names to values. Header mappings also use braces. {"Authorization": "Bearer ..."} sends one header, while {} sends none. response["status"] reads the status entry; response["body"] reads the parsed JSON body.
An assertion states an expectation. Python == compares two values; it is different from the single = used to assign a value. A false assert raises AssertionError, making the test fail. The last line compares the complete expected business object, including the ticket title.
The helper chooses a prepared service from the runner's target argument. You will edit the tests, not the helper or service.
See the Gap in a Passing Test
In this step, you will see why a passing positive test is insufficient.
Run the prepared test runner. Its argument good selects the correct service:
python3 run_tests.py good
Expect test_valid_token with "result": "PASS". The runner collects functions whose names start with test_ and calls each one. It returns exit 0 when all pass, 1 for an assertion failure, and 2 for a test or runtime error.
Two other services are deliberately faulty: missing-auth accepts requests with no Token; wrong-auth accepts a wrong Token. Their positive response still works. Try both:
python3 run_tests.py missing-auth
python3 run_tests.py wrong-auth
Both still pass the only existing test. This result exposes a gap in coverage. The test never sent a rejected request, so it could not notice either bypass.
Add the Missing-Token Requirement
In this step, you will add one test that expects a real unauthenticated request to be rejected.
Open the test file:
nano test_security.py
Keep the positive test and add the new function below it. The complete file should be:
from api import request
def test_valid_token():
response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
assert response["status"] == 200
assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}
def test_missing_token():
response = request("/tickets", {})
assert response["status"] == 401
assert response["body"] == {"error": "unauthorized"}
The empty mapping {} means no request headers. The status assertion checks rejection, while the body assertion ensures the error response contains no ticket data. Keep the four-space indentation. Save with Ctrl+O, Enter, then exit with Ctrl+X.
python3 run_tests.py good
Both tests should pass. Now use the deliberately faulty missing-header service:
python3 run_tests.py missing-auth
Expect test_missing_token to show "result": "FAIL" and "error": "AssertionError". This intentional exit 1 is useful evidence: the new test detects the service accepting a request it should reject. It is different from an import error or a server connection error.
Add the Wrong-Token Requirement
In this step, you will cover a present but invalid credential.
A header can exist without being valid. Testing only an absent header would miss a service that accepts any nonempty Token. Open the same file and add the third function, preserving the first two:
nano test_security.py
Use this complete file:
from api import request
def test_valid_token():
response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
assert response["status"] == 200
assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}
def test_missing_token():
response = request("/tickets", {})
assert response["status"] == 401
assert response["body"] == {"error": "unauthorized"}
def test_wrong_token():
response = request("/tickets", {"Authorization": "Bearer wrong_fixture_token"})
assert response["status"] == 401
assert response["body"] == {"error": "unauthorized"}
wrong_fixture_token is deliberately not accepted by the correct service. Save and exit with Ctrl+O, Enter, Ctrl+X.
python3 run_tests.py good
Expect all three tests to pass. Then test the wrong-header regression:
python3 run_tests.py wrong-auth
Expect only test_wrong_token to fail with AssertionError. Do not change 401 to 200 to make this faulty service pass; that would remove the security requirement.
Validate Both Regression Detections
In this step, you will check the complete test set against all three implementations.
python3 run_tests.py good
python3 run_tests.py missing-auth
python3 run_tests.py wrong-auth
Read the individual results. The correct service must pass all three. The missing-header regression must fail test_missing_token only, and the wrong-header regression must fail test_wrong_token only. The valid-Token business test must continue to pass everywhere.
These failing comparison runs are intentional. A syntax error, connection error, or unconditional failure does not demonstrate detection. Our course check also confirms that the tests sent the three real HTTP requests to each original service.
The comparisons cover these two known regressions. They do not prove every possible authentication or authorization property.
Leave a Passing Correct-Service Run
In this step, you will finish with a repeatable test command for the intended implementation.
cat test_security.py
Confirm the original positive test and both rejection tests remain. Their names describe the three independent requirements.
python3 run_tests.py good
All three results should be PASS. A future delivery gate can run this same command on the intended service. The deliberately faulty services remain available as teaching controls; they are not the implementation to deliver. You have protected real behavior rather than submitting a written claim about test coverage.
Summary
You connected explicit authentication requirements to three real HTTP tests. The correct implementation passes; each prepared bypass is detected by the corresponding rejection assertion. This comparison provides evidence that the tests protect behavior instead of merely executing without errors.



