use new key query param and add some tests (#13)

* first test

* unescape returned keys

* fix to_dict

* wip

* a bit of sync

* dict methods

* lint

* oop

* run the tests

* concurrently

* secret
This commit is contained in:
Sidney Kochman
2020-08-13 19:43:32 -04:00
committed by GitHub
parent 6c1924ddb5
commit 5a056be04c
5 changed files with 203 additions and 8 deletions
+11 -1
View File
@@ -5,8 +5,10 @@ agent:
type: e1-standard-2
os_image: ubuntu1804
blocks:
- name: Lint
- name: Lint and test
task:
secrets:
- name: Repl.it Database
jobs:
- name: flake8
commands:
@@ -15,3 +17,11 @@ blocks:
- python -m pip install --upgrade poetry
- poetry install
- git diff origin/master | poetry run flake8 --diff
- name: unittest
commands:
- sem-version python 3.8
- checkout --use-cache
- python -m pip install --upgrade poetry
- poetry install
- poetry run coverage run -m unittest src/replit/test_database.py
- poetry run coverage report -m
Generated
+54 -3
View File
@@ -76,9 +76,9 @@ version = "1.6.2"
[package.dependencies]
GitPython = ">=1.0.1"
PyYAML = ">=3.13"
colorama = ">=0.3.9"
six = ">=1.10.0"
stevedore = ">=1.20.0"
colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
[[package]]
category = "dev"
@@ -127,11 +127,23 @@ version = "7.1.2"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.4.3"
[[package]]
category = "main"
description = "Code coverage measurement for Python"
name = "coverage"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "5.2.1"
[package.extras]
toml = ["toml"]
[[package]]
category = "dev"
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
@@ -232,6 +244,7 @@ version = "0.18.1"
[package.dependencies]
pycodestyle = "*"
setuptools = "*"
[[package]]
category = "dev"
@@ -505,10 +518,12 @@ Jinja2 = ">=2.3"
Pygments = ">=2.0"
alabaster = ">=0.7,<0.8"
babel = ">=1.3"
colorama = ">=0.3.5"
docutils = ">=0.12"
imagesize = "*"
packaging = "*"
requests = ">=2.5.0"
setuptools = "*"
snowballstemmer = ">=1.1"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
@@ -516,7 +531,6 @@ sphinxcontrib-htmlhelp = "*"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = "*"
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
[package.extras]
docs = ["sphinxcontrib-websupport"]
@@ -696,7 +710,8 @@ idna = ">=2.0"
multidict = ">=4.0"
[metadata]
content-hash = "c92c18a07248bbd0df18fba9327c2387af07b06a9a489123e3276810fb4e89fc"
content-hash = "9ceb6e910237c20a8cf4b40b1c12620752f1e9fe3f0417cba404e4ce2ef10752"
lock-version = "1.0"
python-versions = "^3.8"
[metadata.files]
@@ -758,6 +773,42 @@ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
]
coverage = [
{file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"},
{file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"},
{file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"},
{file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"},
{file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"},
{file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"},
{file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"},
{file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"},
{file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"},
{file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"},
{file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"},
{file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"},
{file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"},
{file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"},
{file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"},
{file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"},
{file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"},
{file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"},
{file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"},
{file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"},
{file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"},
{file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"},
{file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"},
{file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"},
{file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"},
{file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"},
{file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"},
{file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"},
{file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"},
{file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"},
{file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"},
{file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"},
{file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"},
{file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"},
]
darglint = [
{file = "darglint-1.5.2-py3-none-any.whl", hash = "sha256:049a98cf3aec8cf6ea344a863c68112d80b7f8de214459b5fa6853371f89c3e7"},
{file = "darglint-1.5.2.tar.gz", hash = "sha256:6b9461f96694c2cf1d8edb1597a783fe6840953b0eb18cc6cc1e72a26f196d79"},
+1
View File
@@ -16,6 +16,7 @@ flask = "^1.1.2"
werkzeug = "^1.0.1"
aiohttp = "^3.6.2"
nest_asyncio = "^1.4.0"
coverage = "^5.2.1"
[tool.poetry.dev-dependencies]
flake8 = "^3.8.3"
+9 -4
View File
@@ -5,6 +5,7 @@ import json
import os
from sys import stderr
from typing import Any, Callable, Dict, Tuple, Union
import urllib
import aiohttp
import nest_asyncio
@@ -20,7 +21,7 @@ class AsyncJSONKey:
you don't have to do it manually.
"""
__slots__ = ("db", "key", "dtype", "get_default", "discard_bad_data")
__slots__ = ("db", "key", "dtype", "get_default", "discard_bad_data", "do_raise")
def __init__(
self,
@@ -216,14 +217,15 @@ class AsyncReplitDb:
Returns:
Tuple[str]: The keys found.
"""
params = {"prefix": prefix, "encode": "true"}
async with aiohttp.ClientSession() as session:
async with session.get(self.db_url + "?prefix=" + prefix) as response:
async with session.get(self.db_url, params=params) as response:
response.raise_for_status()
text = await response.text()
if not text:
return tuple()
else:
return tuple(text.split("\n"))
return tuple(urllib.parse.unquote(k) for k in text.split("\n"))
async def to_dict(self, prefix: str = "") -> Dict[str, str]:
"""Dump all data in the database into a dictionary.
@@ -238,7 +240,7 @@ class AsyncReplitDb:
ret = {}
keys = await self.list(prefix=prefix)
for i in keys:
ret[i] = await self.view(i)
ret[i] = await self.get(i)
return ret
async def keys(self) -> Tuple[str]:
@@ -263,6 +265,7 @@ class AsyncReplitDb:
dtype: JSON_TYPE,
get_default: Callable = None,
discard_bad_data: bool = False,
do_raise: bool = False,
) -> AsyncJSONKey:
"""Initialize an AsyncJSONKey instance.
@@ -277,6 +280,7 @@ class AsyncReplitDb:
argument is used.
discard_bad_data (bool): Don't prompt if bad data is read, overwrite it
with the default. Defaults to False.
do_raise (bool): Whether to raise exceptions when errors are encountered.
Returns:
AsyncJSONKey: The initialized AsyncJSONKey instance.
@@ -287,6 +291,7 @@ class AsyncReplitDb:
dtype=dtype,
get_default=get_default,
discard_bad_data=discard_bad_data,
do_raise=do_raise,
)
def __repr__(self) -> str:
+128
View File
@@ -0,0 +1,128 @@
"""Tests for replit.database."""
import os
import unittest
from replit.database import AsyncReplitDb, ReplitDb
import requests
class TestAsyncDatabase(unittest.IsolatedAsyncioTestCase):
"""Tests for replit.database.AsyncReplitDb."""
async def asyncSetUp(self) -> None:
"""Grab a JWT for all the tests to share."""
password = os.environ["PASSWORD"]
req = requests.get(
"https://database-test-jwt.kochman.repl.co", auth=("test", password)
)
url = req.text
self.db = AsyncReplitDb(url)
async def asyncTearDown(self) -> None:
"""Nuke whatever the test added."""
for k in await self.db.keys():
await self.db.delete(k)
async def test_get_set_delete(self) -> None:
"""Test that we can get, set, and delete a key."""
await self.db.set("test-key", "value")
val = await self.db.get("test-key")
self.assertEqual(val, "value")
await self.db.delete("test-key")
with self.assertRaises(KeyError):
await self.db.get("test-key")
async def test_list_keys(self) -> None:
"""Test that we can list keys."""
key = "test-list-keys-with\nnewline"
await self.db.set(key, "value")
val = await self.db.get(key)
self.assertEqual(val, "value")
keys = await self.db.list(key)
self.assertEqual(keys, (key,))
await self.db.delete(key)
with self.assertRaises(KeyError):
await self.db.get(key)
async def test_list_values(self) -> None:
"""Test that we can get all values."""
key = "test-list-values"
await self.db.set(key + "1", "value1")
await self.db.set(key + "2", "value2")
vals = await self.db.values()
self.assertTupleEqual(vals, ("value1", "value2"))
async def test_dict(self) -> None:
"""Test that we can get a dict."""
await self.db.set("key1", "value")
await self.db.set("key2", "value")
d = await self.db.to_dict()
self.assertDictEqual(d, {"key1": "value", "key2": "value"})
async def test_jsonkey(self) -> None:
"""Test replit.database.AsyncJSONKey."""
key = "test-jsonkey"
jk = self.db.jsonkey(key, dtype=str, do_raise=True)
with self.assertRaises(KeyError):
await jk.get()
await jk.set("value")
val = await jk.get()
self.assertEqual(val, "value")
async def test_jsonkey_default(self) -> None:
"""Test replit.database.AsyncJSONKey with a default callable."""
key = "test-jsonkey"
jk = self.db.jsonkey(key, dtype=str, get_default=lambda: "value")
val = await jk.get()
self.assertEqual(val, "value")
class TestDatabase(unittest.IsolatedAsyncioTestCase):
"""Tests for replit.database.ReplitDb."""
def setUp(self) -> None:
"""Grab a JWT for all the tests to share."""
password = os.environ["PASSWORD"]
req = requests.get(
"https://database-test-jwt.kochman.repl.co", auth=("test", password)
)
url = req.text
self.db = ReplitDb(url)
async def tearDown(self) -> None:
"""Nuke whatever the test added."""
for k in await self.db.keys():
await self.db.delete(k)
def test_get_set_delete(self) -> None:
"""Test get, set, and delete."""
with self.assertRaises(KeyError):
self.db.get("key")
self.db.set("key", "value")
val = self.db.get("key")
self.assertEqual(val, "value")
self.db.delete("key")
def test_dict(self) -> None:
"""Test using the database as a dict."""
with self.assertRaises(KeyError):
val = self.db["hi"]
self.db["hi"] = "there"
val = self.db.get("hi")
self.assertEqual(val, "there")
del self.db["hi"]
with self.assertRaises(KeyError):
val = self.db["hi"]