Simplified database layer and consolidated Markov writes into a single transaction.
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 5s
CI / Tests (push) Successful in 16s
CI / Type Checking (push) Successful in 10s
CI / Spelling (push) Successful in 5s

This commit is contained in:
2026-03-17 17:07:28 -04:00
parent 56d3c36c7d
commit 6aa8527adc
5 changed files with 109 additions and 219 deletions
+33 -35
View File
@@ -61,42 +61,42 @@ class TestIngestSentence:
async def test_stores_start_word(self, db: Database) -> None:
"""First word of the sentence is stored as a start word."""
await _ingest_sentence(db, 1, 100, "Hello world.")
await db.commit()
result = await db.get_random_start_word(1)
assert result == "Hello"
async def test_stores_transitions(self, db: Database) -> None:
"""Adjacent words create transitions."""
await _ingest_sentence(db, 1, 100, "Hello world.")
await db.commit()
result = await db.get_random_next_word(1, "Hello")
assert result == "world."
async def test_appends_sentence_end_if_missing(self, db: Database) -> None:
"""Unpunctuated sentence gets the default sentence-end marker."""
await _ingest_sentence(db, 1, 100, "Hello world")
await db.commit()
result = await db.get_random_next_word(1, "Hello")
assert result == f"world{DEFAULT_SENTENCE_END}"
async def test_preserves_existing_punctuation(self, db: Database) -> None:
"""Already-punctuated sentence keeps its terminator."""
await _ingest_sentence(db, 1, 100, "Hello world!")
await db.commit()
result = await db.get_random_next_word(1, "Hello")
assert result == "world!"
async def test_single_word_stores_nothing(self, db: Database) -> None:
"""A single-word sentence produces no start words or transitions."""
await _ingest_sentence(db, 1, 100, "Hello.")
await db.commit()
assert await db.get_random_start_word(1) is None
assert await db.get_random_next_word(1, "Hello.") is None
async def test_stores_all_transitions(self, db: Database) -> None:
"""All adjacent word pairs create transitions."""
await _ingest_sentence(db, 1, 100, "A B C.")
await db.commit()
assert await db.get_random_next_word(1, "A") == "B"
assert await db.get_random_next_word(1, "B") == "C."
@@ -107,13 +107,13 @@ class TestIngest:
async def test_single_sentence(self, db: Database) -> None:
"""A single sentence paragraph is ingested."""
await ingest(db, 1, 100, "Hello world.")
await db.commit()
assert await db.get_random_start_word(1) == "Hello"
async def test_multiple_sentences(self, db: Database) -> None:
"""Multiple sentences are split and ingested individually."""
await ingest(db, 1, 100, "Hello world. Goodbye world!")
await db.commit()
# Both "Hello" and "Goodbye" should appear as start words.
async with db._connection.execute(
"SELECT DISTINCT word FROM markov_start_words WHERE channel_id = ?",
@@ -125,14 +125,14 @@ class TestIngest:
async def test_normalizes_whitespace(self, db: Database) -> None:
"""Extra spaces and newlines are collapsed."""
await ingest(db, 1, 100, "Hello world.\nGoodbye world!")
await db.commit()
result = await db.get_random_next_word(1, "Hello")
assert result == "world."
async def test_appends_default_end(self, db: Database) -> None:
"""Unpunctuated paragraph gets the default sentence-end marker."""
await ingest(db, 1, 100, "Hello world")
await db.commit()
result = await db.get_random_next_word(1, "Hello")
assert result == f"world{DEFAULT_SENTENCE_END}"
@@ -148,13 +148,13 @@ class TestIngest:
) -> None:
"""Empty or whitespace-only input does not store any data."""
await ingest(db, 1, 100, paragraph)
await db.commit()
assert await db.get_random_start_word(1) is None
async def test_splits_on_punctuation_followed_by_space(self, db: Database) -> None:
"""Punctuation followed by a space splits into separate sentences."""
await ingest(db, 1, 100, "Dr. Smith likes cats")
await db.commit()
# "Dr." splits off as a single-word sentence (stores nothing).
# "Smith likes cats" becomes a sentence with "Smith" as start word.
assert await db.get_random_start_word(1) == "Smith"
@@ -162,7 +162,7 @@ class TestIngest:
async def test_no_split_without_space_after_punctuation(self, db: Database) -> None:
"""Punctuation not followed by a space keeps words together."""
await ingest(db, 1, 100, "Hello.World is here")
await db.commit()
assert await db.get_random_start_word(1) == "Hello.World"
@@ -177,14 +177,14 @@ class TestGenerate:
async def test_generates_from_ingested_data(self, db: Database) -> None:
"""Generated text uses words from ingested data."""
await ingest(db, 1, 100, "The quick brown fox.")
await db.commit()
result = await generate(db, 1)
assert result == "The quick brown fox."
async def test_strips_section_sign(self, db: Database) -> None:
"""The internal section sign marker never appears in output."""
await ingest(db, 1, 100, "Hello world")
await db.commit()
result = await generate(db, 1)
assert result == "Hello world"
@@ -193,7 +193,7 @@ class TestGenerate:
words = [f"w{i}" for i in range(100)]
text = " ".join(words)
await ingest(db, 1, 100, text)
await db.commit()
result = await generate(db, 1, soft_limit=10, hard_limit=49)
assert result == "w0 w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 w13 w14"
@@ -204,8 +204,8 @@ class TestGenerate:
# the loop falls back to get_random_next_word for each. D has both a
# continuing ("E") and completing ("end.") transition, so
# get_random_completing_next_word deterministically picks "end.".
await db.add_start_words_batch([(1, 100, "A")])
await db.add_transitions_batch(
await db.add_markov_data(
[(1, 100, "A")],
[
(1, 100, "A", "B"),
(1, 100, "B", "C"),
@@ -215,24 +215,24 @@ class TestGenerate:
(1, 100, "D", "G"),
(1, 100, "D", "H"),
(1, 100, "D", "end."),
]
],
)
await db.commit()
for _ in range(100):
result = await generate(db, 1, soft_limit=1, hard_limit=1000)
assert result == "A B C D end."
async def test_start_word_already_ends_sentence(self, db: Database) -> None:
"""Generation stops immediately when the start word is sentence-ending."""
await db.add_start_words_batch([(1, 100, "Yes.")])
await db.commit()
await db.add_markov_data([(1, 100, "Yes.")], [])
result = await generate(db, 1)
assert result == "Yes."
async def test_chain_dead_end(self, db: Database) -> None:
"""Generation stops when no next word exists (dead-end chain)."""
await db.add_start_words_batch([(1, 100, "Hello")])
await db.commit()
await db.add_markov_data([(1, 100, "Hello")], [])
# "Hello" has no transitions, so the loop breaks immediately.
result = await generate(db, 1)
assert result == "Hello"
@@ -240,14 +240,14 @@ class TestGenerate:
async def test_hard_limit_strips_section_sign(self, db: Database) -> None:
"""Section sign at the truncation boundary is stripped."""
# Build a chain: "A" -> "B" -> "C§".
await db.add_start_words_batch([(1, 100, "A")])
await db.add_transitions_batch(
await db.add_markov_data(
[(1, 100, "A")],
[
(1, 100, "A", "B"),
(1, 100, "B", f"C{DEFAULT_SENTENCE_END}"),
]
],
)
await db.commit()
# hard_limit=5 truncates "A B C§" (length 6) to "A B C".
result = await generate(db, 1, soft_limit=100, hard_limit=5)
assert result == "A B C"
@@ -258,17 +258,15 @@ class TestGenerate:
"""After soft_limit, falls back when no completing word exists."""
# "A" -> "B" (no completing transition). Past soft_limit,
# get_random_completing_next_word returns None, falls back to "B".
await db.add_start_words_batch([(1, 100, "A")])
await db.add_transitions_batch([(1, 100, "A", "B")])
await db.commit()
await db.add_markov_data([(1, 100, "A")], [(1, 100, "A", "B")])
result = await generate(db, 1, soft_limit=1, hard_limit=1000)
assert result == "A B"
async def test_hard_limit_truncates_mid_word(self, db: Database) -> None:
"""Hard limit slices output even when it falls inside a word."""
await db.add_start_words_batch([(1, 100, "AB")])
await db.add_transitions_batch([(1, 100, "AB", "CDEF")])
await db.commit()
await db.add_markov_data([(1, 100, "AB")], [(1, 100, "AB", "CDEF")])
# "AB CDEF" is 7 chars; hard_limit=5 truncates to "AB CD".
result = await generate(db, 1, soft_limit=100, hard_limit=5)
assert result == "AB CD"
@@ -277,7 +275,7 @@ class TestGenerate:
"""Data ingested into one channel does not leak into another."""
await ingest(db, 1, 100, "Channel one data.")
await ingest(db, 2, 100, "Channel two data.")
await db.commit()
# Channel 3 has no data; should get the fallback.
result = await generate(db, 3)
assert result == "Hello world!"