A real run from BuildGist’s demo project, shown as BuildGist explained it. What happened is BuildGist’s own record of the run. The explanation below it was written by AI.
Add share links to notes. POST /notes/<id>/share should return a link anyone can open to read that…
What happened recorded by BuildGist
- The developer asked
Add share links to notes. POST /notes/<id>/share should return a link anyone can open to read that note. Write tests and run them. After this I want links that expire after 7 days, don't start that yet.
- Files changed
- 3 files +46 −2
Show files
- modified
test_app.py+26 −0 - modified
app.py+19 −1 - modified
README.md+1 −1
- modified
- Tests run
.venv/bin/python -m pytest -qpassed 7 passed
Explanation
Written by AI. Check important behavior against the code and tests.
Added note share links
Added a share endpoint that returns a link anyone can open to read a note, with tests.
What changed
POST to the share address for a note returns a token and a full URL containing it, with status 201. GET on the shared address looks the token up in an in-memory mapping and returns the note as JSON, or 404 if the token or note is missing. Before this run, the only read paths were the list and per-id GET routes.
Share links for notes
Each note can now get a share link: a POST to its share address returns a token and a full link, and opening that link returns the note as JSON.
Show code
app.py 47 abort(404) 48 return "", 204 49 50 @app.post("/notes/<int:note_id>/share") 51 def share_note(note_id: int): 52 if note_id not in notes: 53 abort(404) 54 token = [redacted]) 55 shares[token] = note_id 56 url = url_for("read_shared_note", token=[redacted], _external=True) 57 return jsonify({"token": token, "url": url}), 201 58
app.py 56 url = url_for("read_shared_note", token=[redacted], _external=True) 57 return jsonify({"token": token, "url": url}), 201 58 59 @app.get("/shared/<token>") 60 def read_shared_note(token: str): 61 note = notes.get(shares.get(token)) 62 if note is None: 63 abort(404) 64 return jsonify(note) 65 66 return app
app.py 10def create_app() -> Flask: 11 app = Flask(__name__) 12 notes: dict[int, dict] = {} 13 shares: dict[str, int] = {} # token -> note id 14 next_id = 1 15 16 @app.get("/notes")
Tests for share links
Four new tests cover reading a note through its link, sharing a missing note, an unknown token, and a link that stops working after its note is deleted.
Show code
test_app.py 26 assert client.get(f"/notes/{note_id}").status_code == 404 27 28 29def test_share_link_reads_note(client): 30 note_id = client.post("/notes", json={"title": "Recipe", "body": "flour"}).get_json()["id"] 31 shared = client.post(f"/notes/{note_id}/share") 32 assert shared.status_code == 201 33 url = shared.get_json()["url"] 34 assert url.startswith("http://localhost/shared/") 35 opened = client.get(url) 36 assert opened.status_code == 200 37 assert opened.get_json()["body"] == "flour"
test_app.py 45 assert client.get("/shared/not-a-token").status_code == 404 46 47 48def test_share_link_dies_with_note(client): 49 note_id = client.post("/notes", json={"title": "Temp"}).get_json()["id"] 50 url = client.post(f"/notes/{note_id}/share").get_json()["url"] 51 client.delete(f"/notes/{note_id}") 52 assert client.get(url).status_code == 404
Endpoint documentation
The README's endpoint list now includes the share endpoint and the shared-note endpoint, alongside the existing note endpoints.
Show code
README.md 6 .venv/bin/python -m flask --app app run # http://127.0.0.1:5000/notes 7 .venv/bin/python -m pytest -q # tests 8 9Endpoints: `GET /notes`, `POST /notes` (`{"title", "body"}`), `GET /notes/<id>`, `DELETE /notes/<id>`, `POST /notes/<id>/share` (returns `{"token", "url"}`), `GET /shared/<token>`.
What this means
- Share links never expire and cannot be revoked. Expiry after seven days is the next step you asked for and is not in this run.
- The test runner reported 7 passed for the command pytest -q. The four share tests are the new ones in test_app.py.
- The changed files app.py, test_app.py and README.md are recorded as not attributable to the agent.
Concerns
The line that creates each share token is masked in the shown code, so it cannot be checked whether the token is hard to guess, and anyone who guesses one can read the note. Medium
share_note assigns the token from a value that was masked before this message was sent, and the link is built from that same value. read_shared_note checks only that the token is in the shares mapping, with no other check. Whether the link is safe depends entirely on how the token is generated, which the message does not show.
Show evidence
47 abort(404) 48 return "", 204 49 50 @app.post("/notes/<int:note_id>/share") 51 def share_note(note_id: int): 52 if note_id not in notes: 53 abort(404) 54 token = [redacted]) 55 shares[token] = note_id 56 url = url_for("read_shared_note", token=[redacted], _external=True) 57 return jsonify({"token": token, "url": url}), 201 58
56 url = url_for("read_shared_note", token=[redacted], _external=True) 57 return jsonify({"token": token, "url": url}), 201 58 59 @app.get("/shared/<token>") 60 def read_shared_note(token: str): 61 note = notes.get(shares.get(token)) 62 if note is None: 63 abort(404) 64 return jsonify(note) 65 66 return app
Share tokens are never removed from the shares mapping when their note is deleted, so stale entries accumulate. Low
More
delete_note pops only the note from notes. The shares mapping is not touched, so every stale token stays as a key. The test test_share_link_dies_with_note shows that a link returns 404 after its note is deleted, because the note lookup returns nothing.
Show evidence
10def create_app() -> Flask: 11 app = Flask(__name__) 12 notes: dict[int, dict] = {} 13 shares: dict[str, int] = {} # token -> note id 14 next_id = 1 15 16 @app.get("/notes")
45 assert client.get("/shared/not-a-token").status_code == 404 46 47 48def test_share_link_dies_with_note(client): 49 note_id = client.post("/notes", json={"title": "Temp"}).get_json()["id"] 50 url = client.post(f"/notes/{note_id}/share").get_json()["url"] 51 client.delete(f"/notes/{note_id}") 52 assert client.get(url).status_code == 404