mager-bench

GPT-OSS 120B × test-writing

6.3

Edge-case thinking + pytest parametrize discipline

correctness 5.0quality 7.0documentation 7.02854ms
$ cat test-writing.prompt — what the model was asked
Write a comprehensive pytest test suite for the following Python function:

```python
def parse_duration(s: str) -> int:
    """Parse a human duration string into total seconds.

    Accepted formats: '1h', '30m', '45s', '1h30m', '2h15m30s'.
    Raises ValueError on unrecognized input.
    """
    import re
    pattern = r'^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$'
    m = re.fullmatch(pattern, s.strip())
    if not m or not s.strip():
        raise ValueError(f'Invalid duration: {s!r}')
    h, mi, sec = (int(x) if x else 0 for x in m.groups())
    return h * 3600 + mi * 60 + sec
```

Requirements:
- Use pytest (plain functions, no unittest classes)
- Use `@pytest.mark.parametrize` for the happy-path cases
- Cover at least 3 edge/error cases with `pytest.raises`
- Name tests descriptively so failures are self-explaining
- No mocking needed — the function is pure
- Do not reimplement the function; test it as a black box

run 1 of 1

6.3
correctness 5.0quality 7.0documentation 7.02854ms

# judge: Happy-path parametrization is thorough and arithmetically correct, and most error cases correctly raise ValueError, but the added test_parse_duration_non_string_input_raises_type_error is factually wrong—passing an int would raise AttributeError (since s.strip() is called before any type check), not TypeError—so this test would actually fail when run, undermining the suite's correctness despite otherwise good structure and naming.

$ cat gpt-oss-120b.test-writing.r1.response
Below is a **stand‑alone** pytest test module that exercises `parse_duration`.  
It follows every requirement you listed:

* plain test functions (no `unittest.TestCase` inheritance)  
* happy‑path cases are driven by a single `@pytest.mark.parametrize` call  
* at least three distinct error‑path checks use `pytest.raises`  
* each test name describes what is being verified, so a failure message is self‑explanatory  
* no mocking – the function is treated as a black box  

```python
# test_parse_duration.py
import pytest

# ----------------------------------------------------------------------
# Import the function under test.
# Adjust the import path to match where `parse_duration` lives in your project.
# ----------------------------------------------------------------------
from your_module import parse_duration   # <-- replace `your_module` as needed


# ----------------------------------------------------------------------
# HAPPY‑PATH TESTS
# ----------------------------------------------------------------------
@pytest.mark.parametrize(
    "input_str, expected_seconds",
    [
        # simple single‑unit strings
        ("1h",      3600),
        ("30m",      1800),
        ("45s",        45),

        # mixed units – hour + minute
        ("2h15m",  2 * 3600 + 15 * 60),

        # mixed units – hour + minute + second
        ("2h15m30s", 2 * 3600 + 15 * 60 + 30),

        # minute + second (no hour)
        ("5m20s", 5 * 60 + 20),

        # hour + second (no minute)
        ("3h7s", 3 * 3600 + 7),

        # all zeroes – should return 0
        ("0h0m0s", 0),

        # leading zeros are allowed
        ("00h01m02s", 60 + 2),

        # whitespace around the string is ignored (strip)
        ("  1h30m  ", 1 * 3600 + 30 * 60),

        # missing units are interpreted as zero
        ("4h", 4 * 3600),
        ("12m", 12 * 60),
        ("9s", 9),
    ],
)
def test_parse_duration_happy_path(input_str: str, expected_seconds: int) -> None:
    """
    Verify that valid duration strings are parsed into the correct number of seconds.
    """
    assert parse_duration(input_str) == expected_seconds


# ----------------------------------------------------------------------
# ERROR‑PATH TESTS
# ----------------------------------------------------------------------
@pytest.mark.parametrize(
    "bad_input",
    [
        "",                     # empty string
        "   ",                  # only whitespace
        "1x",                   # unknown unit
        "1h30",                 # missing unit after number
        "30m1h",                # wrong order – minutes before hours
        "h",                    # missing number before unit
        "-5m",                  # negative numbers are not allowed
        "5m-10s",               # negative component in the middle
        "1h 30m",               # internal space (pattern does not allow)
        "2h15m30sextra",        # trailing garbage
    ],
)
def test_parse_duration_invalid_inputs_raise(bad_input: str) -> None:
    """
    Ensure that any malformed or unsupported duration string raises ValueError.
    """
    with pytest.raises(ValueError, match="Invalid duration"):
        parse_duration(bad_input)


# ----------------------------------------------------------------------
# ADDITIONAL EDGE CASES (optional but illustrative)
# ----------------------------------------------------------------------
def test_parse_duration_all_missing_units_raises() -> None:
    """
    An empty match (e.g. an empty string after stripping) should raise.
    """
    with pytest.raises(ValueError):
        parse_duration("")


def test_parse_duration_non_string_input_raises_type_error() -> None:
    """
    The function expects a `str`. Passing a non‑string should raise a TypeError
    (the underlying `re.fullmatch` will complain).
    """
    with pytest.raises(TypeError):
        parse_duration(123)          # type: ignore[arg-type]


# ----------------------------------------------------------------------
# End of test module
# ----------------------------------------------------------------------
```

### How to run

```bash
pytest -v test_parse_duration.py
```

All happy‑path cases should pass, and every intentionally malformed input should trigger a `ValueError`, confirming that `parse_duration` behaves as documented. Adjust the import line (`from your_module import parse_duration`) to point to the actual location of the function in your code base.