usawa

Signed, immutable accounting.
Log | Files | Refs | Submodules | LICENSE

commit c8acc262fa7732872aacd9446b39b65b6e04dc88
parent 761532d2fac616dd5160cc8030e71a6bc1e26e34
Author: lash <dev@holbrook.no>
Date:   Sun, 17 May 2026 15:22:04 -0600

Add tests for linker object

Diffstat:
Mdummy/tests/link.py | 49++++++++++++++++++++++++++++++++++++++++++++++---
Mdummy/usawa/link.py | 50++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 94 insertions(+), 5 deletions(-)

diff --git a/dummy/tests/link.py b/dummy/tests/link.py @@ -2,6 +2,7 @@ import logging import datetime import unittest import os +import uuid from usawa import Entry, Ledger, UnitIndex from usawa.link import EntryLink @@ -15,11 +16,11 @@ class TestLink(unittest.TestCase): def setUp(self): uidx = UnitIndex() - ledger = Ledger(uidx) - self.linker = EntryLink(ledger) + self.ledger = Ledger(uidx) + self.linker = EntryLink(self.ledger) - def test_register(self): + def test_dup(self): o = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC)) self.linker.link(o) o = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) @@ -29,5 +30,47 @@ class TestLink(unittest.TestCase): self.linker.link(o) + def test_explicit(self): + uu = uuid.uuid4() + o = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) + self.linker.link(o) + o = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) + self.linker.link(o, link_uuid=uu) + o = Entry(serial=1337, tx_date=datetime.datetime.now(datetime.UTC)) + self.linker.link(o, link_uuid=uu) + r = self.linker.get_for(o) + self.assertEqual(len(r), 2) + self.assertEqual(r[0], 42) + self.assertEqual(r[1], 666) + r = self.linker.get(o) + self.assertEqual(r, str(uu)) + + + def test_implicit(self): + uu = uuid.uuid4() + entry_a = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) + entry_b = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) + self.linker.link_to(entry_a, entry_b) + r = self.linker.get_for(entry_a) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], 666) + r = self.linker.get_for(entry_b) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], 42) + + + def test_implicit_ref(self): + linker = EntryLink(self.ledger, refs=True) + uu = uuid.uuid4() + entry_a = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) + entry_b = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) + linker.link_to(entry_a, entry_b) + o = Entry(serial=0, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) + r = linker.get_for(o) + self.assertEqual(len(r), 2) + self.assertEqual(r[0], 42) + self.assertEqual(r[1], 666) + + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/link.py b/dummy/usawa/link.py @@ -1,11 +1,19 @@ import uuid +import logging + +from .entry import Entry + +logg = logging.getLogger('link') class EntryLink: - def __init__(self, ledger): + def __init__(self, ledger, refs=False): self.ledger = ledger self.idx = {} + self.idx_ref = None + if refs: + self.idx_ref = {} self.links = {} @@ -14,9 +22,47 @@ class EntryLink: if v != None: raise FileExistsError('link {} already has entry {}'.format(v, entry)) if link_uuid == None: - link_uuid = uuid.uuid4() + link_uuid = entry.ref s = str(link_uuid) if self.links.get(s) == None: self.links[s] = [] self.links[s].append(entry.serial) self.idx[entry.serial] = s + if self.idx_ref != None: + self.idx_ref[entry.ref] = s + logg.debug('link {} added entry {}'.format(link_uuid, entry)) + + + def link_to(self, anchor, entry): + if not isinstance(anchor, Entry): + anchor = Entry.empty(serial=anchor.serial) + v = self.get(anchor) + if v == None: + v = anchor.ref + if v == None: + v = str(uuid.uuid4()) + self.link(anchor, link_uuid=v) + self.link(entry, link_uuid=v) + return v + + + def get_for(self, entry): + v = self.get(entry) + if v == None: + return None + r = [] + logg.debug('link {} found for entry {}'.format(v, entry)) + for v in self.links[v]: + if v == entry.serial: + continue + r.append(v) + return r + + + def get(self, entry): + v = self.idx.get(entry.serial) + if v != None: + return v + if self.idx_ref == None: + return None + return self.idx_ref.get(entry.ref)