From b7e3156936cba204ddaa915a43c0d21af08f1591 Mon Sep 17 00:00:00 2001 From: fullsend-code Date: Fri, 5 Jun 2026 08:36:19 +0000 Subject: [PATCH 1/2] fix(#7): use auto-incrementing counter for item IDs The create_item() function generated IDs via len(_get_items()) + 1, which produced duplicate IDs after deletions reduced the list length. Replace the length-based ID with a module-level monotonically increasing counter (_next_id) that never reuses values. The reset_items() helper resets the counter for test isolation. Added test_no_id_collision_after_delete to verify that creating an item after a deletion does not produce a colliding ID. Closes #7 --- app.py | 12 +++++++++++- test_app.py | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index a2d2561..b982d20 100644 --- a/app.py +++ b/app.py @@ -27,7 +27,7 @@ def create_item(): return jsonify(error="name is required"), 400 item = { - "id": len(_get_items()) + 1, + "id": _new_id(), "name": data["name"], "done": False, } @@ -63,6 +63,14 @@ def delete_item(item_id): _items_store = [] +_next_id = 1 + + +def _new_id(): + global _next_id + current = _next_id + _next_id += 1 + return current def _get_items(): @@ -71,7 +79,9 @@ def _get_items(): def reset_items(): """Reset the in-memory store (used by tests).""" + global _next_id _items_store.clear() + _next_id = 1 if __name__ == "__main__": diff --git a/test_app.py b/test_app.py index d5ae8f6..6eb64a0 100644 --- a/test_app.py +++ b/test_app.py @@ -63,3 +63,26 @@ def test_delete_item(client): def test_delete_item_not_found(client): resp = client.delete("/items/999") assert resp.status_code == 404 + + +def test_no_id_collision_after_delete(client): + """IDs must never be reused after deletion.""" + resp1 = client.post("/items", json={"name": "A"}) + resp2 = client.post("/items", json={"name": "B"}) + id_a = resp1.get_json()["id"] + id_b = resp2.get_json()["id"] + + # Delete first item + client.delete(f"/items/{id_a}") + + # Create a new item — should NOT collide with B's id + resp3 = client.post("/items", json={"name": "C"}) + id_c = resp3.get_json()["id"] + assert id_c != id_b, "New item ID collides with existing item" + + # Verify both remaining items exist in the list + all_items = client.get("/items").get_json()["items"] + all_ids = [item["id"] for item in all_items] + assert id_b in all_ids + assert id_c in all_ids + assert len(all_items) == 2 From 7abaaf64d2f97513aa1f8e7aa588f879f2cc35c8 Mon Sep 17 00:00:00 2001 From: fullsend-fix Date: Fri, 5 Jun 2026 08:56:47 +0000 Subject: [PATCH 2/2] fix: use itertools.count for thread-safe IDs & add multi-delete test - Replace manual global counter with itertools.count(1) for effectively atomic ID generation under CPython (addresses race-condition finding). - Add test_no_id_reuse_after_multiple_deletes to cover bulk delete+create edge case (addresses test-adequacy finding). Addresses review feedback on #10 Signed-off-by: fullsend-fix --- app.py | 13 ++++++------- test_app.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index b982d20..5251c5a 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,5 @@ +import itertools + from flask import Flask, jsonify, request from datetime import datetime, timezone @@ -63,14 +65,11 @@ def delete_item(item_id): _items_store = [] -_next_id = 1 +_id_counter = itertools.count(1) def _new_id(): - global _next_id - current = _next_id - _next_id += 1 - return current + return next(_id_counter) def _get_items(): @@ -79,9 +78,9 @@ def _get_items(): def reset_items(): """Reset the in-memory store (used by tests).""" - global _next_id + global _id_counter _items_store.clear() - _next_id = 1 + _id_counter = itertools.count(1) if __name__ == "__main__": diff --git a/test_app.py b/test_app.py index 6eb64a0..29f29a6 100644 --- a/test_app.py +++ b/test_app.py @@ -86,3 +86,25 @@ def test_no_id_collision_after_delete(client): assert id_b in all_ids assert id_c in all_ids assert len(all_items) == 2 + + +def test_no_id_reuse_after_multiple_deletes(client): + """IDs stay unique after several deletions followed by several creates.""" + # Create three items + ids = [] + for name in ("X", "Y", "Z"): + resp = client.post("/items", json={"name": name}) + ids.append(resp.get_json()["id"]) + + # Delete all three + for item_id in ids: + client.delete(f"/items/{item_id}") + + # Create two more items — their IDs must not collide with any prior ID + new_ids = [] + for name in ("P", "Q"): + resp = client.post("/items", json={"name": name}) + new_ids.append(resp.get_json()["id"]) + + all_assigned = ids + new_ids + assert len(all_assigned) == len(set(all_assigned)), "ID reuse detected"