seed: curriculum content
This commit is contained in:
27
1.solar-system/3.strings/0.index.md
Normal file
27
1.solar-system/3.strings/0.index.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
type: module
|
||||
title: "Strings"
|
||||
description: "Text is half of every program — index it, slice it, transform it, count it, format it."
|
||||
---
|
||||
|
||||
# Strings
|
||||
|
||||
Half of what programs do is move text around. Reading user input,
|
||||
formatting reports, parsing log lines, validating passwords — all
|
||||
strings. This module hands you the toolkit.
|
||||
|
||||
Seven blocks. About three and a half hours. No conditionals or loops
|
||||
yet — those come next module — so every challenge here works through
|
||||
indexing, slicing, methods, and built-in functions.
|
||||
|
||||
## What You'll Do
|
||||
|
||||
- Index and slice strings
|
||||
- Apply the most-used string methods (`upper`, `strip`, `split`,
|
||||
`replace`, ...)
|
||||
- Validate, count, mirror, and format text
|
||||
- Build a small mission report from raw fields
|
||||
|
||||
## Pace
|
||||
|
||||
About 3.5 hours. Each block is a small real program — no drills.
|
||||
42
1.solar-system/3.strings/01.the-message/index.md
Normal file
42
1.solar-system/3.strings/01.the-message/index.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Message"
|
||||
xp: 50
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Message
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, a string is a *sequence of characters*. You can pick any
|
||||
> one out by its position (its *index*).
|
||||
>
|
||||
> Indexes start at 0:
|
||||
>
|
||||
> ```
|
||||
> M I S S I O N
|
||||
> 0 1 2 3 4 5 6
|
||||
> ```
|
||||
>
|
||||
> Negative indexes count back from the end. `s[-1]` is the last
|
||||
> character.
|
||||
>
|
||||
> Strings are *immutable* — you can read any character, but you can't
|
||||
> change one in place. To "modify" a string, you build a new one.
|
||||
>
|
||||
> Implement `info(s)` that returns a dict with four keys: `first`,
|
||||
> `sixth`, `last`, `length`.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
Open `starter/starter.py`. Use `s[0]`, `s[5]`, `s[-1]`, and `len(s)`
|
||||
to fill in the dict.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `info("MISSION-CONTROL-7")` returns `{"first": "M", "sixth": "O", "last": "7", "length": 17}`
|
||||
- Works for any non-empty string of length ≥ 6
|
||||
13
1.solar-system/3.strings/01.the-message/starter/starter.py
Normal file
13
1.solar-system/3.strings/01.the-message/starter/starter.py
Normal file
@@ -0,0 +1,13 @@
|
||||
def info(s):
|
||||
"""Return a dict describing the string s.
|
||||
|
||||
Keys:
|
||||
"first" — the character at index 0
|
||||
"sixth" — the character at index 5
|
||||
"last" — the character at index -1
|
||||
"length" — total number of characters
|
||||
|
||||
info("MISSION-CONTROL-7") -> {"first": "M", "sixth": "O",
|
||||
"last": "7", "length": 17}
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,16 @@
|
||||
from solution import info
|
||||
|
||||
|
||||
def test_mission_control():
|
||||
r = info("MISSION-CONTROL-7")
|
||||
assert r == {"first": "M", "sixth": "O", "last": "7", "length": 17}
|
||||
|
||||
|
||||
def test_andromeda():
|
||||
r = info("andromeda")
|
||||
assert r == {"first": "a", "sixth": "m", "last": "a", "length": 9}
|
||||
|
||||
|
||||
def test_alphabet():
|
||||
r = info("ABCDEFGH")
|
||||
assert r == {"first": "A", "sixth": "F", "last": "H", "length": 8}
|
||||
45
1.solar-system/3.strings/02.the-slicer/index.md
Normal file
45
1.solar-system/3.strings/02.the-slicer/index.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Slicer"
|
||||
xp: 50
|
||||
duration: 25
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Slicer
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, *slicing* grabs a chunk of a string instead of one character.
|
||||
> The syntax is `s[start:end]` — `start` is included, `end` is not.
|
||||
>
|
||||
> ```python
|
||||
> s = "ANDROMEDA"
|
||||
> s[0:3] # "AND"
|
||||
> s[3:7] # "ROME"
|
||||
> s[-2:] # "DA"
|
||||
> ```
|
||||
>
|
||||
> Telemetry packets always arrive in this exact format (length 30):
|
||||
>
|
||||
> ```
|
||||
> TIMESTAMP=YYYY-MM-DDTHH:MM:SSZ
|
||||
> ```
|
||||
>
|
||||
> Implement `extract(header)` that returns a tuple `(date, time)`:
|
||||
>
|
||||
> - `date` = `YYYY-MM-DD` (10 chars)
|
||||
> - `time` = `HH:MM:SS` (8 chars)
|
||||
>
|
||||
> Figure out which slice positions hold each piece.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, return `(date, time)` extracted with slicing.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `extract("TIMESTAMP=2026-05-04T14:32:01Z")` → `("2026-05-04", "14:32:01")`
|
||||
- Works for any header in the same fixed format
|
||||
16
1.solar-system/3.strings/02.the-slicer/starter/starter.py
Normal file
16
1.solar-system/3.strings/02.the-slicer/starter/starter.py
Normal file
@@ -0,0 +1,16 @@
|
||||
def extract(header):
|
||||
"""Extract date and time from a fixed-format packet header.
|
||||
|
||||
Header format (always exactly this shape):
|
||||
TIMESTAMP=YYYY-MM-DDTHH:MM:SSZ
|
||||
Length 30. Example:
|
||||
TIMESTAMP=2026-05-04T14:32:01Z
|
||||
|
||||
Return a tuple (date, time) where:
|
||||
date = "YYYY-MM-DD" (10 chars)
|
||||
time = "HH:MM:SS" (8 chars)
|
||||
|
||||
extract("TIMESTAMP=2026-05-04T14:32:01Z")
|
||||
-> ("2026-05-04", "14:32:01")
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,13 @@
|
||||
from solution import extract
|
||||
|
||||
|
||||
def test_basic():
|
||||
assert extract("TIMESTAMP=2026-05-04T14:32:01Z") == ("2026-05-04", "14:32:01")
|
||||
|
||||
|
||||
def test_apollo():
|
||||
assert extract("TIMESTAMP=1969-07-20T13:32:00Z") == ("1969-07-20", "13:32:00")
|
||||
|
||||
|
||||
def test_end_of_century():
|
||||
assert extract("TIMESTAMP=2099-12-31T23:59:59Z") == ("2099-12-31", "23:59:59")
|
||||
43
1.solar-system/3.strings/03.the-methods/index.md
Normal file
43
1.solar-system/3.strings/03.the-methods/index.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Methods"
|
||||
xp: 50
|
||||
duration: 30
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Methods
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, every string carries a toolkit of methods. The most-used:
|
||||
>
|
||||
> - `s.upper()` — uppercase copy
|
||||
> - `s.lower()` — lowercase copy
|
||||
> - `s.strip()` — drops leading/trailing whitespace
|
||||
> - `s.replace(a, b)` — every `a` becomes `b`
|
||||
>
|
||||
> Methods can be *chained* — each returns a new string you can call
|
||||
> the next on:
|
||||
>
|
||||
> ```python
|
||||
> " Hello World ".strip().lower().replace(" ", "_")
|
||||
> # → "hello_world"
|
||||
> ```
|
||||
>
|
||||
> Implement `transform(s)` that returns a dict with three keys:
|
||||
>
|
||||
> - `upper` — input uppercased
|
||||
> - `lower` — input lowercased
|
||||
> - `clean` — input stripped, lowercased, spaces replaced by `_`
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, build the dict using the four methods.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `transform(" Hello World ")["clean"]` returns `"hello_world"`
|
||||
- All three keys present and correct for any string
|
||||
15
1.solar-system/3.strings/03.the-methods/starter/starter.py
Normal file
15
1.solar-system/3.strings/03.the-methods/starter/starter.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def transform(s):
|
||||
"""Apply three string transformations and return them in a dict.
|
||||
|
||||
Keys:
|
||||
"upper" — s.upper()
|
||||
"lower" — s.lower()
|
||||
"clean" — s stripped of leading/trailing whitespace,
|
||||
lowercased, with spaces replaced by underscores
|
||||
|
||||
transform(" Hello World ") returns:
|
||||
{"upper": " HELLO WORLD ",
|
||||
"lower": " hello world ",
|
||||
"clean": "hello_world"}
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,22 @@
|
||||
from solution import transform
|
||||
|
||||
|
||||
def test_padded_hello_world():
|
||||
r = transform(" Hello World ")
|
||||
assert r["upper"] == " HELLO WORLD "
|
||||
assert r["lower"] == " hello world "
|
||||
assert r["clean"] == "hello_world"
|
||||
|
||||
|
||||
def test_mission_ready():
|
||||
r = transform("MISSION READY")
|
||||
assert r["upper"] == "MISSION READY"
|
||||
assert r["lower"] == "mission ready"
|
||||
assert r["clean"] == "mission_ready"
|
||||
|
||||
|
||||
def test_andromeda_sector():
|
||||
r = transform("andromeda sector")
|
||||
assert r["upper"] == "ANDROMEDA SECTOR"
|
||||
assert r["lower"] == "andromeda sector"
|
||||
assert r["clean"] == "andromeda_sector"
|
||||
44
1.solar-system/3.strings/04.the-validator/index.md
Normal file
44
1.solar-system/3.strings/04.the-validator/index.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Validator"
|
||||
xp: 75
|
||||
duration: 30
|
||||
difficulty: 2
|
||||
---
|
||||
|
||||
# The Validator
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, every system that takes user input has to *validate* it.
|
||||
>
|
||||
> Comparison operators (`>=`, `<`, `==`) return `True` or `False`
|
||||
> directly. String methods like `.isupper()` and `.isdigit()` also
|
||||
> return booleans:
|
||||
>
|
||||
> ```python
|
||||
> len("hello") >= 8 # False
|
||||
> "Hello"[0].isupper() # True
|
||||
> "abc1"[-1].isdigit() # True
|
||||
> ```
|
||||
>
|
||||
> Implement `validate(pw)` that returns a dict with four checks on
|
||||
> a non-empty password:
|
||||
>
|
||||
> - `length` — char count (int)
|
||||
> - `long_enough` — `length >= 8` (bool)
|
||||
> - `starts_capital` — first char uppercase (bool)
|
||||
> - `ends_digit` — last char a digit (bool)
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, return the dict using `len()`, `>=`,
|
||||
`.isupper()`, `.isdigit()`.
|
||||
|
||||
## Objectives
|
||||
|
||||
- All four keys present with correct types
|
||||
- `validate("Andromeda1")` matches the expected dict exactly
|
||||
- Works for any non-empty password
|
||||
17
1.solar-system/3.strings/04.the-validator/starter/starter.py
Normal file
17
1.solar-system/3.strings/04.the-validator/starter/starter.py
Normal file
@@ -0,0 +1,17 @@
|
||||
def validate(pw):
|
||||
"""Check four properties of a non-empty password and return a dict.
|
||||
|
||||
Keys:
|
||||
"length" — number of characters (int)
|
||||
"long_enough" — True if length >= 8 (bool)
|
||||
"starts_capital" — True if first char is uppercase (bool)
|
||||
"ends_digit" — True if last char is a digit (bool)
|
||||
|
||||
Helpers:
|
||||
pw[0].isupper() — True if first char is uppercase
|
||||
pw[-1].isdigit() — True if last char is a digit
|
||||
|
||||
validate("Andromeda1") -> {"length": 10, "long_enough": True,
|
||||
"starts_capital": True, "ends_digit": True}
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,41 @@
|
||||
from solution import validate
|
||||
|
||||
|
||||
def test_strong_password():
|
||||
r = validate("Andromeda1")
|
||||
assert r == {
|
||||
"length": 10,
|
||||
"long_enough": True,
|
||||
"starts_capital": True,
|
||||
"ends_digit": True,
|
||||
}
|
||||
|
||||
|
||||
def test_short_lowercase_no_digit():
|
||||
r = validate("abc")
|
||||
assert r == {
|
||||
"length": 3,
|
||||
"long_enough": False,
|
||||
"starts_capital": False,
|
||||
"ends_digit": False,
|
||||
}
|
||||
|
||||
|
||||
def test_capital_no_digit():
|
||||
r = validate("Hello")
|
||||
assert r == {
|
||||
"length": 5,
|
||||
"long_enough": False,
|
||||
"starts_capital": True,
|
||||
"ends_digit": False,
|
||||
}
|
||||
|
||||
|
||||
def test_long_lowercase_with_digit():
|
||||
r = validate("spaceX2026")
|
||||
assert r == {
|
||||
"length": 10,
|
||||
"long_enough": True,
|
||||
"starts_capital": False,
|
||||
"ends_digit": True,
|
||||
}
|
||||
43
1.solar-system/3.strings/05.the-mirror/index.md
Normal file
43
1.solar-system/3.strings/05.the-mirror/index.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Mirror"
|
||||
xp: 75
|
||||
duration: 30
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Mirror
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, a *palindrome* reads the same forwards and backwards.
|
||||
> `racecar`. `level`. `madam`.
|
||||
>
|
||||
> A reverse-slice flips a string in one move:
|
||||
>
|
||||
> ```python
|
||||
> "Hello"[::-1] # "olleH"
|
||||
> ```
|
||||
>
|
||||
> A string is a palindrome when it equals its reverse. The `==`
|
||||
> operator returns `True` or `False` directly.
|
||||
>
|
||||
> Implement `mirror(s)` that returns a dict with three keys:
|
||||
>
|
||||
> - `original` — the input
|
||||
> - `reversed` — the input reversed
|
||||
> - `palindrome` — `True` if equal to its reverse
|
||||
>
|
||||
> Match case exactly — `racecar` and `RaceCar` are different inputs.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, build the dict using `[::-1]` and `==`.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `mirror("racecar")["palindrome"]` is `True`
|
||||
- `mirror("hello")["palindrome"]` is `False`
|
||||
- All three keys correct for any input string
|
||||
19
1.solar-system/3.strings/05.the-mirror/starter/starter.py
Normal file
19
1.solar-system/3.strings/05.the-mirror/starter/starter.py
Normal file
@@ -0,0 +1,19 @@
|
||||
def mirror(s):
|
||||
"""Detect if s is a palindrome and return a dict.
|
||||
|
||||
Keys:
|
||||
"original" — s unchanged
|
||||
"reversed" — s reversed
|
||||
"palindrome" — True if s equals its reverse, False otherwise
|
||||
|
||||
Reverse a string with [::-1]:
|
||||
"Hello"[::-1] -> "olleH"
|
||||
|
||||
mirror("racecar") -> {"original": "racecar",
|
||||
"reversed": "racecar",
|
||||
"palindrome": True}
|
||||
mirror("hello") -> {"original": "hello",
|
||||
"reversed": "olleh",
|
||||
"palindrome": False}
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
from solution import mirror
|
||||
|
||||
|
||||
def test_palindrome():
|
||||
r = mirror("racecar")
|
||||
assert r == {"original": "racecar", "reversed": "racecar", "palindrome": True}
|
||||
|
||||
|
||||
def test_not_palindrome():
|
||||
r = mirror("hello")
|
||||
assert r == {"original": "hello", "reversed": "olleh", "palindrome": False}
|
||||
|
||||
|
||||
def test_level():
|
||||
r = mirror("level")
|
||||
assert r["palindrome"] is True
|
||||
|
||||
|
||||
def test_andromeda():
|
||||
r = mirror("andromeda")
|
||||
assert r["palindrome"] is False
|
||||
assert r["reversed"] == "ademordna"
|
||||
|
||||
|
||||
def test_noon():
|
||||
r = mirror("noon")
|
||||
assert r == {"original": "noon", "reversed": "noon", "palindrome": True}
|
||||
35
1.solar-system/3.strings/06.the-counter/index.md
Normal file
35
1.solar-system/3.strings/06.the-counter/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Counter"
|
||||
xp: 75
|
||||
duration: 30
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Counter
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, time to count things in text. Three counts:
|
||||
>
|
||||
> - **Total length** — `len(s)`
|
||||
> - **Word count** — `len(s.split())` (split breaks on whitespace)
|
||||
> - **`a` count** — `s.lower().count("a")` returns how many `a`s
|
||||
>
|
||||
> Implement `count(s)` that returns a dict with three keys:
|
||||
>
|
||||
> - `length` — total chars including spaces
|
||||
> - `words` — word count
|
||||
> - `a_count` — number of lowercase `a`s after lowercasing
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, build the dict using `len()`, `.split()`,
|
||||
`.lower()`, `.count()`.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `count("Hello andromeda")` → `{"length": 15, "words": 2, "a_count": 2}`
|
||||
- All three keys present and correct for any sentence
|
||||
16
1.solar-system/3.strings/06.the-counter/starter/starter.py
Normal file
16
1.solar-system/3.strings/06.the-counter/starter/starter.py
Normal file
@@ -0,0 +1,16 @@
|
||||
def count(s):
|
||||
"""Count three things in a sentence and return a dict.
|
||||
|
||||
Keys:
|
||||
"length" — total characters in s (including spaces)
|
||||
"words" — number of whitespace-separated words
|
||||
"a_count" — count of lowercase 'a's after lowercasing s
|
||||
|
||||
Helpers:
|
||||
len(s) — char count
|
||||
len(s.split()) — word count
|
||||
s.lower().count("a") — number of 'a's, case-insensitive
|
||||
|
||||
count("Hello andromeda") -> {"length": 15, "words": 2, "a_count": 2}
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,21 @@
|
||||
from solution import count
|
||||
|
||||
|
||||
def test_hello_andromeda():
|
||||
assert count("Hello andromeda") == {"length": 15, "words": 2, "a_count": 2}
|
||||
|
||||
|
||||
def test_all_systems_nominal():
|
||||
assert count("All systems nominal") == {"length": 19, "words": 3, "a_count": 2}
|
||||
|
||||
|
||||
def test_caps_with_a():
|
||||
assert count("MISSION READY ALPHA") == {"length": 19, "words": 3, "a_count": 3}
|
||||
|
||||
|
||||
def test_quick_brown_fox():
|
||||
assert count("the quick brown fox jumps over") == {
|
||||
"length": 30,
|
||||
"words": 6,
|
||||
"a_count": 0,
|
||||
}
|
||||
47
1.solar-system/3.strings/07.the-report/index.md
Normal file
47
1.solar-system/3.strings/07.the-report/index.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
type: challenge
|
||||
title: "The Report"
|
||||
xp: 100
|
||||
duration: 35
|
||||
difficulty: 3
|
||||
---
|
||||
|
||||
# The Report
|
||||
|
||||
> **[INCOMING — Mission Control, Earth]**
|
||||
>
|
||||
> Cadet, capstone for the strings module. Combine f-strings,
|
||||
> repetition, methods, and `\n` joining into one formatted output.
|
||||
>
|
||||
> Repetition: `"=" * 30` produces a string of 30 equals signs.
|
||||
>
|
||||
> Implement `format_report(name, status, location, time)` that
|
||||
> returns this exact 8-line report as a single string (lines joined
|
||||
> with `\n`):
|
||||
>
|
||||
> ```
|
||||
> ==============================
|
||||
> MISSION REPORT
|
||||
> ==============================
|
||||
> Name: <NAME IN UPPERCASE>
|
||||
> Status: <status as entered>
|
||||
> Location: <location as entered>
|
||||
> Time: <time as entered>
|
||||
> ==============================
|
||||
> ```
|
||||
>
|
||||
> Only the name is uppercased; other fields print as entered.
|
||||
>
|
||||
> [END TRANSMISSION]
|
||||
|
||||
## Your Task
|
||||
|
||||
In `starter/starter.py`, build the report string. Use `"=" * 30`
|
||||
for borders, `name.upper()` for the name, and join lines with `\n`.
|
||||
|
||||
## Objectives
|
||||
|
||||
- `format_report("apollo", "ok", "moon", "13:32")` returns the
|
||||
exact 8-line string
|
||||
- Only the name is uppercased
|
||||
- Borders are 30 `=` characters
|
||||
19
1.solar-system/3.strings/07.the-report/starter/starter.py
Normal file
19
1.solar-system/3.strings/07.the-report/starter/starter.py
Normal file
@@ -0,0 +1,19 @@
|
||||
def format_report(name, status, location, time):
|
||||
"""Return a formatted mission report as a single string.
|
||||
|
||||
The report is 8 lines, joined by '\n'. The borders are 30 equals
|
||||
signs ("=" * 30). Only the name is uppercased; other fields are
|
||||
inserted as-is.
|
||||
|
||||
format_report("apollo", "ok", "moon", "13:32") returns:
|
||||
|
||||
==============================
|
||||
MISSION REPORT
|
||||
==============================
|
||||
Name: APOLLO
|
||||
Status: ok
|
||||
Location: moon
|
||||
Time: 13:32
|
||||
==============================
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,36 @@
|
||||
from solution import format_report
|
||||
|
||||
|
||||
def test_apollo():
|
||||
expected = (
|
||||
"==============================\n"
|
||||
"MISSION REPORT\n"
|
||||
"==============================\n"
|
||||
"Name: APOLLO\n"
|
||||
"Status: ok\n"
|
||||
"Location: moon\n"
|
||||
"Time: 13:32\n"
|
||||
"=============================="
|
||||
)
|
||||
assert format_report("apollo", "ok", "moon", "13:32") == expected
|
||||
|
||||
|
||||
def test_voyager():
|
||||
result = format_report("voyager", "active", "interstellar", "1977-09-05")
|
||||
assert "Name: VOYAGER" in result
|
||||
assert "Status: active" in result
|
||||
assert "Location: interstellar" in result
|
||||
assert "Time: 1977-09-05" in result
|
||||
assert result.startswith("=" * 30 + "\n")
|
||||
assert result.endswith("\n" + "=" * 30)
|
||||
|
||||
|
||||
def test_perseverance():
|
||||
result = format_report("perseverance", "roving", "jezero crater", "2026-05-04")
|
||||
assert "Name: PERSEVERANCE" in result
|
||||
assert "Location: jezero crater" in result
|
||||
|
||||
|
||||
def test_eight_lines():
|
||||
result = format_report("apollo", "ok", "moon", "13:32")
|
||||
assert result.count("\n") == 7
|
||||
Reference in New Issue
Block a user