Files
versioning/tests/test_units.py
T

118 lines
4.4 KiB
Python
Raw Normal View History

import asyncio
from pathlib import PurePath
from versiond.db import Database
from versiond.diff import hunks, unified
from versiond.filters import Filters
from versiond.ingest import Coalescer, Repository
from versiond.store import BlobStore
def test_filters_paths():
f = Filters()
ok = ["app.py", "src/main.go", ".env", ".env.production", "Makefile", "pkg/package.json", ".gitignore",
# user data: images, archives, databases, documents (backed up as raw files)
"photos/vacation.jpg", "img/logo.png", "backup/site.tar.gz", "data/archive.zip",
"app.db", "app.sqlite3", "report.pdf", "song.mp3", "clip.mp4"]
for p in ok:
assert f.path_reason(PurePath(p)) is None, p
journals = ["app.db-wal", "app.db-shm", "app.sqlite-journal", "data/x-wal"]
for p in journals:
assert f.path_reason(PurePath(p)) == "database-journal", p
rejected = {
"node_modules/x/index.js": "ignored-directory",
".git/config": "ignored-directory",
"src/__pycache__/a.cpython-312.pyc": "ignored-directory",
"a.pyc": "ignored-extension",
".bashrc_local": "hidden-file",
"notes.txt~": "temporary-file",
"main.py.swp": "ignored-extension",
"draft.temp": "ignored-extension",
"x.kate-swp": "ignored-extension",
"~$lock.docx": "temporary-file",
"#autosave#": "temporary-file",
"venv/lib/x.py": "ignored-directory",
"target/debug/build.rs": "ignored-directory",
"4913": "temporary-file",
"src/pkg.egg-info/PKG-INFO": "ignored-directory",
}
for p, reason in rejected.items():
assert f.path_reason(PurePath(p)) == reason, p
assert f.size_reason(10_485_760) is None
assert f.size_reason(10_485_761) == "file-too-large"
def test_filter_patterns():
f = Filters(patterns=("*.log", "secret/*"))
assert f.path_reason(PurePath("x/debug.log")) == "ignored-pattern"
assert f.path_reason(PurePath("secret/key.txt")) == "ignored-pattern"
assert f.path_reason(PurePath("src/key.txt")) is None
def test_directory_patterns():
f = Filters(patterns=("app/data/uploads",))
assert f.skip_dir("uploads", "app/data/uploads")
assert not f.skip_dir("uploads", "other/uploads")
assert f.path_reason(PurePath("app/data/uploads/a/b.txt")) == "ignored-pattern"
assert f.path_reason(PurePath("app/data/config.json")) is None
def test_diff_unified():
a = b"one\ntwo\nthree\n"
b = b"one\n2\nthree\nfour\n"
out = unified(hunks(a, b), "a", "b")
assert "-two\n+2\n" in out and "+four\n" in out
assert hunks(b"x y\n", b"x y\n", ignore_whitespace=True) == []
def _repo(tmp_path):
db = Database(tmp_path / "index.sqlite")
return Repository(db, BlobStore(tmp_path / "spool"), Filters())
def test_coalescer_keeps_first_and_last(tmp_path):
repo = _repo(tmp_path)
path = str(tmp_path / "f.py")
async def run():
c = Coalescer(repo, window=0.2, max_hold=1.0, per_hour=100)
statuses = [c.submit(path, f"v{i}\n".encode(), "test", "edit")["status"] for i in range(6)]
await asyncio.sleep(0.5)
return statuses
statuses = asyncio.run(run())
assert statuses[0] == "committed"
assert set(statuses[1:]) == {"coalesced"}
file = repo.db.file_by_path(path)
contents = [repo.blobs.get(v["blob_sha256"]) for v in reversed(repo.db.history(file["id"]))]
assert contents == [b"v0\n", b"v5\n"]
def test_coalescer_rate_limit_keeps_newest(tmp_path):
repo = _repo(tmp_path)
path = str(tmp_path / "f.py")
async def run():
c = Coalescer(repo, window=0.05, max_hold=0.1, per_hour=2)
c.submit(path, b"a", "t", "e")
await asyncio.sleep(0.1)
c.submit(path, b"b", "t", "e")
await asyncio.sleep(0.2)
status = c.submit(path, b"c", "t", "e")["status"]
await asyncio.sleep(0.2)
flushed = c.flush()
return status, flushed
status, flushed = asyncio.run(run())
assert status == "coalesced"
assert flushed == 1 # over the limit it waits, and shutdown still stores it
file = repo.db.file_by_path(path)
assert repo.blobs.get(repo.db.latest_version(file["id"])["blob_sha256"]) == b"c"
def test_dedupe_unchanged(tmp_path):
repo = _repo(tmp_path)
path = str(tmp_path / "f.py")
assert repo.commit(path, b"x", "t", "e")["status"] == "committed"
assert repo.commit(path, b"x", "t", "e")["status"] == "unchanged"