3.15 Random Values HW
3.15 Random Values: Homework Notebook
Lesson: CSP 3.15 Random Values (SFI Backend QA Test Garage) Contents: Popcorn Hacks 1-3 (with predictions + output), MCQ result, Homework Hack (SFI Backend QA Simulator), College Board pseudocode check, submission notes.
Popcorn Hack 1: Random SFI Record Number
Prediction: random.randint(1, 4) includes both endpoints, so every run prints 1, 2, 3, or 4. The same number can appear twice in a row.
Code Runner Challenge
Popcorn Hack 1 - Random SFI Record Number
View IPYNB Source
# CODE_RUNNER: Popcorn Hack 1 - Random SFI Record Number
import random
record_count = 4
# One random record number from 1 through record_count (inclusive)
record_number = random.randint(1, record_count)
print("SFI record selected for QA:", record_number)
# Run it many times to see the pattern of outputs
picks = [random.randint(1, record_count) for _ in range(12)]
print("12 more picks:", picks)
# Every generated value must be a valid record number
assert all(1 <= p <= record_count for p in picks)
print("All picks valid (1 to", record_count, "):", all(1 <= p <= record_count for p in picks))
# Repeated values are normal because each pick is independent
repeats = any(picks[i] == picks[i + 1] for i in range(len(picks) - 1))
print("Back-to-back repeat in these picks:", repeats)
Explanation: The complete set of possible outputs is {1, 2, 3, 4}. randint is inclusive, so both 1 and 4 can be returned. Repeats are allowed because each call is an independent random selection, not a rotation through the list. Seeing only a few runs doesnโt prove other values are impossible.
Popcorn Hack 2: Random Structured SFI Car Part
Prediction: One of the three records is printed whole: its product name, category, and spec number always belong together (for example, spec 1.2 always goes with Multiple Disc Clutch Assemblies and Drag Racing).
Code Runner Challenge
Popcorn Hack 2 - Random SFI Part Record
View IPYNB Source
# CODE_RUNNER: Popcorn Hack 2 - Random SFI Part Record
import random
parts = [
{
"product_name": "Replacement Flywheels and Clutch Assemblies",
"category": "Auto Racing",
"spec_number": "1.1"
},
{
"product_name": "Multiple Disc Clutch Assemblies",
"category": "Drag Racing",
"spec_number": "1.2"
},
{
"product_name": "Racing Flywheel Record",
"category": "Auto Racing",
"spec_number": "2.1"
}
]
# random.choice picks one COMPLETE dictionary, so its fields stay together
selected = random.choice(parts)
print("Selected record for testing:")
print(" Product: ", selected["product_name"])
print(" Category: ", selected["category"])
print(" Spec no.: ", selected["spec_number"])
# Change one input and compare: run several picks and confirm each record stays intact
print()
for i in range(3):
p = random.choice(parts)
assert p in parts # the chosen object is exactly one of the original records
print(f"Pick {i + 1}: {p['spec_number']} -> {p['product_name']} ({p['category']})")
Explanation: Choosing a whole dictionary keeps the record intact. Choosing separate random names, categories, and spec numbers could mix fields from different parts and create an invalid record.
Popcorn Hack 3: Random SFI API Test
Prediction: Each run picks one part and one action. GET/search should report a lookup, POST/create should report that the part is validated before creating a record, and PUT/update should report an update for that part. There are 3 parts x 3 actions = 9 possible part/action pairs.
Code Runner Challenge
Popcorn Hack 3 - Random SFI API Test
View IPYNB Source
# CODE_RUNNER: Popcorn Hack 3 - Random SFI API Test
import random
parts = [
{"product_name": "Replacement Flywheels", "spec_number": "1.1"},
{"product_name": "Multiple Disc Clutch Assemblies", "spec_number": "1.2"},
{"product_name": "Racing Flywheel Record", "spec_number": "2.1"}
]
actions = [
"GET/search",
"POST/create",
"PUT/update"
]
def run_test(part, action):
if action == "GET/search":
return f"Searching for spec {part['spec_number']} -> found '{part['product_name']}'"
elif action == "POST/create":
return f"Validating '{part['product_name']}' (spec {part['spec_number']}) before creating a new record"
else: # PUT/update
return f"Updating the stored record for spec {part['spec_number']} ({part['product_name']})"
# One random test
part = random.choice(parts)
action = random.choice(actions)
print(f"[{action}] {run_test(part, action)}")
# Change the action to see different branches: run 6 more random tests
print()
for _ in range(6):
part = random.choice(parts)
action = random.choice(actions)
print(f"[{action:<11}] {run_test(part, action)}")
Explanation: The random action flows through an if / elif / else, so a different random value takes a different branch of the program. Testing it only once would show just one of the 9 possible outcomes.
In-class MCQ Check
| # | Question | My answer | Why |
|---|---|---|---|
| 1 | What can random.randint(2, 5) return? |
C (2, 3, 4, 5) | randint includes both endpoints. |
| 2 | Record A appears twice in a row. What does it mean? | B | Repeats are allowed in independent random selection. |
| 3 | Python match for RANDOM(1,4)? |
B (random.randint(1,4)) |
Integer from 1 through 4 inclusive. |
| 4 | Why is testing a random program once insufficient? | B | One run shows only one of many possible outcomes. |
Code Runner Challenge
Homework Hack - SFI Backend QA Simulator
View IPYNB Source
MCQ 3.15: 4/4 | answers: C,B,B,B
Homework Hack: SFI Backend QA Simulator
A reusable randomized QA simulator. Each run randomly selects a complete part record and a backend action, then returns a backend-style response. It tracks the stored spec numbers, so creating a duplicate is rejected, and updating or removing a record that isnโt stored returns โnot foundโ.
# CODE_RUNNER: Homework Hack - SFI Backend QA Simulator
import random
parts = [
{
"product_name": "Replacement Flywheels",
"category": "Auto Racing",
"spec_number": "1.1"
},
{
"product_name": "Multiple Disc Clutch Assemblies",
"category": "Drag Racing",
"spec_number": "1.2"
},
{
"product_name": "Racing Flywheel Record",
"category": "Auto Racing",
"spec_number": "2.1"
}
]
actions = [
"GET/search",
"POST/create",
"PUT/update",
"DELETE/remove"
]
existing_spec_numbers = ["1.1", "2.1"]
test_count = 5
def simulate_action(part, action, stored):
"""
Simulate one backend call.
Parameters:
part: one complete SFI record (dictionary with product_name, category, spec_number)
action: one of "GET/search", "POST/create", "PUT/update", "DELETE/remove"
stored: list of spec numbers currently in the backend (changed by create/remove)
Returns:
a backend-style response string
"""
spec = part["spec_number"]
name = part["product_name"]
in_db = spec in stored
if action == "GET/search":
return f"200 OK: found {name} (spec {spec})" if in_db else f"404 NOT FOUND: spec {spec} is not stored"
elif action == "POST/create":
if in_db:
return f"409 CONFLICT: spec {spec} already exists, create rejected"
stored.append(spec)
return f"201 CREATED: {name} saved with new spec {spec}"
elif action == "PUT/update":
return f"200 OK: updated {name} (spec {spec})" if in_db else f"404 NOT FOUND: cannot update spec {spec}"
else: # DELETE/remove
if in_db:
stored.remove(spec)
return f"200 OK: removed spec {spec} ({name})"
return f"404 NOT FOUND: cannot remove spec {spec}"
def run_qa(count, stored_start):
stored = list(stored_start) # work on a copy so each simulation starts clean
for run in range(1, count + 1):
part = random.choice(parts) # random complete record
action = random.choice(actions) # random API action
result = simulate_action(part, action, stored)
print(f"Run {run}: {action:<13} on spec {part['spec_number']} -> {result}")
print("Stored spec numbers after run:", sorted(stored))
print("=== QA simulation: 5 random tests ===")
run_qa(test_count, existing_spec_numbers)
print()
print("=== Second simulation (different random results) ===")
run_qa(test_count, existing_spec_numbers)
# Larger run to show that every part/action combination is reachable
print()
print("=== 300 random picks: combinations reached ===")
seen = set()
for _ in range(300):
seen.add((random.choice(parts)["spec_number"], random.choice(actions)))
print(f"Distinct (record, action) pairs seen: {len(seen)} of {len(parts) * len(actions)} possible")
Possible outputs and duplicate handling
random.choice(parts)has 3 possible outcomes andrandom.choice(actions)has 4, so there are 3 x 4 = 12 possible record/action pairs. A pair can repeat on consecutive runs. Because the stored list changes during a run (create adds, remove deletes), the same pair can legitimately give different responses later.- Duplicate-spec handling:
POST/createchecksspec in storedfirst. Spec1.1or2.1is already stored, so create returns 409 CONFLICT; spec1.2is new, so it returns 201 CREATED the first time and 409 if it is created again. - Records stay intact because I choose a whole dictionary rather than picking fields independently.
College Board Pseudocode Check
Choose recordIndex = 2 and actionNumber = 3. Tracing by hand: actionNumber = 1 is false, actionNumber = 2 is false, actionNumber = 3 is true, so the program displays PUT/update for parts[2] (Multiple Disc Clutch Assemblies, spec 1.2). In Python, random.choice(actions) can produce "PUT/update" too, which is the same decision reached by a different route. RANDOM(1, LENGTH(parts)) and random.choice(parts) both select one of the 3 records, and since pseudocode lists start at index 1 while Python lists start at 0, randint(1, 3) would need to be adjusted to parts[index - 1].
Submission Notes (for the Notes field)
Lesson: CSP 3.15 Random Values
Notebook: <published URL>
MCQ 3.15: 4/4 | answers: C,B,B,B
Popcorn Hacks 1-3 completed during class and copied to notebook: yes
Homework tested: ran the simulator several times (5 tests each) plus a 300-pick run; all 12 record/action pairs reached, duplicate create returned 409, new create returned 201