model.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import os
  2. import peewee
  3. import pytest
  4. os.environ["LC_DB_PATH"] = ":memory:"
  5. os.environ["LC_SECRET_KEY"] = "TEST_KEY"
  6. os.environ["LC_APP_PATH"] = "localhost"
  7. import lc.config as c
  8. import lc.error as e
  9. import lc.request as r
  10. import lc.model as m
  11. class Testdb:
  12. def setup_method(self, _):
  13. c.app.in_memory_db()
  14. m.create_tables()
  15. def teardown_method(self, _):
  16. c.app.close_db()
  17. def mk_user(self, name="gdritter", password="foo") -> m.User:
  18. return m.User.from_request(
  19. r.User(
  20. name=name,
  21. password=password,
  22. )
  23. )
  24. def test_create_user(self):
  25. name = "gdritter"
  26. u = self.mk_user(name=name)
  27. # it should be the only thing in the db
  28. all_users = m.User.select()
  29. assert len(all_users) == 1
  30. assert all_users[0].id == u.id
  31. assert all_users[0].name == name
  32. # we should be able to find it with the given name, too
  33. named_user = m.User.get(m.User.name == name)
  34. assert named_user.id == u.id
  35. assert named_user.name == name
  36. def test_user_passwords(self):
  37. name = "gdritter"
  38. password = "foo"
  39. u = self.mk_user(name=name, password=password)
  40. print(u.name, u.passhash)
  41. assert u.authenticate(password)
  42. assert u.authenticate("wrong password") is False
  43. def test_no_duplicate_users(self):
  44. name = "gdritter"
  45. u1 = self.mk_user(name=name)
  46. with pytest.raises(e.UserExists):
  47. u2 = self.mk_user(name=name)
  48. def test_get_or_create_tag(self):
  49. u = self.mk_user()
  50. tag_name = "food"
  51. t = m.Tag.get_or_create_tag(u, tag_name)
  52. # we should be able to find the tag with the given name
  53. named_tags = m.Tag.select(m.Tag.user == u and m.Tag.name == tag_name)
  54. assert len(named_tags) == 1
  55. # subsequent calls to get_or_create_tag should return the same db row
  56. t2 = m.Tag.get_or_create_tag(u, tag_name)
  57. assert t.id == t2.id
  58. def test_find_hierarchy(self):
  59. u = self.mk_user()
  60. t = m.Tag.get_or_create_tag(u, "food/bread/rye")
  61. # this should have created three db rows: for 'food', for
  62. # 'food/bread', and for 'food/bread/rye':
  63. assert len(m.Tag.select()) == 3
  64. # searching for a prefix of the tag should yield the same
  65. # parent tag
  66. assert t.parent.id == m.Tag.get(name="food/bread").id
  67. assert t.parent.parent.id == m.Tag.get(name="food").id
  68. # creating a new hierarchical tag with a shared prefix should
  69. # only create the new child tag
  70. t2 = m.Tag.get_or_create_tag(u, "food/bread/baguette")
  71. print([t.name for t in m.Tag.select()])
  72. assert len(m.Tag.select()) == 4
  73. # it should share the same parent tags
  74. assert t2.parent.id == t.parent.id
  75. assert t2.parent.parent.id == t.parent.parent.id
  76. # trying to get a hierarchical tag should result in the same
  77. # one already entered
  78. assert t.id == m.Tag.get(name="food/bread/rye").id
  79. assert t2.id == m.Tag.get(name="food/bread/baguette").id
  80. def test_add_hierarchy(self):
  81. u = self.mk_user()
  82. req = r.Link("http://foo.com", "foo", "", False, ["food/bread/rye"])
  83. l = m.Link.from_request(u, req)
  84. assert l.name == req.name
  85. tag_names = {t.tag.name for t in l.tags} # type: ignore
  86. assert tag_names == {"food", "food/bread", "food/bread/rye"}
  87. def test_bad_tag(self):
  88. u = self.mk_user()
  89. req = r.Link("http://foo.com", "foo", "", False, ["foo{bar}"])
  90. with pytest.raises(e.BadTagName):
  91. l = m.Link.from_request(u, req)
  92. def test_create_invite(self):
  93. u = self.mk_user()
  94. invite = m.UserInvite.manufacture(u)
  95. # the invite should reference the user and be unclaimed
  96. assert invite.created_by.id == u.id
  97. assert invite.created_at is not None
  98. assert invite.claimed_by is None
  99. assert invite.claimed_at is None
  100. # deserializing the unique token should reveal the encrypted data
  101. raw_data = c.app.load_token(invite.token)
  102. assert raw_data["created_by"] == u.name
  103. def test_use_invite(self):
  104. u = self.mk_user()
  105. initial_invite = m.UserInvite.manufacture(u)
  106. assert initial_invite.claimed_by is None
  107. assert initial_invite.claimed_at is None
  108. u2 = m.User.from_invite(r.User(name="u2", password="u2"), initial_invite.token)
  109. invite = m.UserInvite.by_code(initial_invite.token)
  110. assert invite.token == initial_invite.token
  111. assert invite.created_by.id == u.id
  112. assert invite.claimed_by.id == u2.id
  113. assert invite.created_at is not None
  114. assert invite.claimed_at is not None
  115. def bad_use_invite(self):
  116. initial_invite = m.UserInvite.manufacture(self.mk_user())
  117. # creating this user claims the invite
  118. m.User.from_invite(r.User(name="u2", password="u2"), initial_invite.token)
  119. # using the invite again raise an error
  120. with pytest.raises(e.AlreadyUsedInvite):
  121. m.User.from_invite(r.User(name="u3", password="u3"), initial_invite.token)
  122. with pytest.raises(e.NoSuchInvite):
  123. m.User.from_invite(r.User(name="u4", password="u4"), "a-non-existent-token")
  124. def check_tags(self, l, tags):
  125. present = set(map(lambda hastag: hastag.tag.name, l.tags))
  126. assert present == set(tags)
  127. def test_edit_link(self):
  128. u = self.mk_user()
  129. req = r.Link("http://foo.com", "foo", "", False, ["foo", "bar"])
  130. l = m.Link.from_request(u, req)
  131. assert l.name == req.name
  132. assert l.tags == ["foo", "bar"] # type: ignore
  133. # check the in-place update
  134. req.name = "bar"
  135. req.tags = ["bar", "baz"]
  136. req.private = True
  137. l.update_from_request(u, req)
  138. assert l.name == req.name
  139. assert l.private
  140. assert l.created != req.created
  141. self.check_tags(l, req.tags)
  142. # check that the link was persisted
  143. l2 = m.Link.by_id(l.id)
  144. assert l2
  145. assert l2.name == req.name
  146. assert l2.private
  147. assert l2.created != req.created
  148. self.check_tags(l2, req.tags)