-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from sitzz/15-add-tests
15 add tests
- Loading branch information
Showing
23 changed files
with
846 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,4 @@ | |
.idea | ||
build/* | ||
*.egg-info | ||
|
||
__pycache__ |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,4 @@ | |
|
||
__all__ = ["CouchbaseHelper", "Session", "Timeout", "BucketNotSet", "ScopeNotSet"] | ||
__author__ = "Thomas 'sitzz' Vang <[email protected]>" | ||
__version__ = "0.0.9b" | ||
__version__ = "0.0.10b" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from couchbase_helper.fake.bucket import Bucket | ||
from couchbase_helper.fake.cluster import Cluster | ||
from couchbase_helper.fake.collection import Collection | ||
from couchbase_helper.fake.scope import Scope | ||
from couchbase_helper.fake.session import Session | ||
|
||
__all__ = [ | ||
"Bucket", | ||
"Cluster", | ||
"Collection", | ||
"Scope", | ||
"Session", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import datetime | ||
import sys | ||
|
||
|
||
def utcnow(): | ||
if sys.version_info >= (3, 12): | ||
from datetime import UTC | ||
|
||
return datetime.datetime.now(UTC) | ||
else: | ||
return datetime.datetime.utcnow() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from couchbase.logic.bucket import BucketLogic | ||
|
||
from couchbase_helper.fake.collection import Collection | ||
from couchbase_helper.fake.scope import Scope | ||
|
||
|
||
class Bucket(BucketLogic): | ||
def __init__(self, cluster, bucket_name): | ||
super().__init__(cluster, bucket_name) | ||
self._connected = False | ||
|
||
def close(self): | ||
if self.connected: | ||
super()._open_or_close_bucket(open_bucket=False) | ||
self._destroy_connection() | ||
|
||
def default_scope(self): | ||
return self.scope(Scope.default_name()) | ||
|
||
def scope(self, name): | ||
return Scope(self, name) | ||
|
||
def collection(self, collection_name): | ||
scope = self.default_scope() | ||
return scope.collection(collection_name) | ||
|
||
def default_collection(self): | ||
scope = self.default_scope() | ||
return scope.collection(Collection.default_name()) | ||
|
||
def ping(self): | ||
pass | ||
|
||
def view_query(self, design_doc, view_name, *view_options, **kwargs): | ||
pass | ||
|
||
def collections(self): | ||
# return CollectionManager(self.connection, self.name) | ||
pass | ||
|
||
def view_indexes(self): | ||
# return ViewIndexManager(self.connection, self.name) | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from datetime import timedelta | ||
from time import sleep, time | ||
from random import randint | ||
|
||
from couchbase.exceptions import UnAmbiguousTimeoutException | ||
from couchbase.logic.cluster import ClusterLogic | ||
from couchbase.serializer import DefaultJsonSerializer | ||
from couchbase.transcoder import JSONTranscoder | ||
|
||
from couchbase_helper.fake._datetime_hack import utcnow | ||
from couchbase_helper.fake.bucket import Bucket | ||
|
||
|
||
class Cluster(ClusterLogic): | ||
def __init__(self, connstr, *options, **kwargs): | ||
super().__init__(connstr, *options, **kwargs) | ||
self._connected = False | ||
self._default_serializer = DefaultJsonSerializer() | ||
self._transcoder = JSONTranscoder() | ||
|
||
def close(self): | ||
self._connected = False | ||
|
||
def bucket(self, bucket_name): | ||
return Bucket(self, bucket_name) | ||
|
||
def cluster_info(self): | ||
pass | ||
|
||
def ping(self, *opts, **kwargs): | ||
pass | ||
|
||
def diagnostics(self, *opts, **kwargs): | ||
pass | ||
|
||
@staticmethod | ||
def wait_until_ready(timeout: timedelta, *opts, **kwargs): | ||
cutoff = (utcnow() + timeout).timestamp() | ||
wait = 1 / randint(100, 500) | ||
if time() + wait < cutoff: | ||
sleep(wait) | ||
return | ||
|
||
sleep(timeout.seconds) | ||
raise UnAmbiguousTimeoutException | ||
|
||
def query(self, statement, *options, **kwargs): | ||
pass | ||
|
||
def analytics_query(self, statement, *options, **kwargs): | ||
pass | ||
|
||
def search_query(self, index, query, *options, **kwargs): | ||
pass | ||
|
||
def search(self, index, request, *options, **kwargs): | ||
pass | ||
|
||
def buckets(self): | ||
pass | ||
|
||
def users(self): | ||
pass | ||
|
||
def query_indexes(self): | ||
pass | ||
|
||
def analytics_indexes(self): | ||
pass | ||
|
||
def search_indexes(self): | ||
pass | ||
|
||
def eventing_functions(self): | ||
pass | ||
|
||
def connect(self, connstr, *options, **kwargs): | ||
self._connected = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
from datetime import timedelta | ||
from time import time_ns | ||
|
||
from couchbase.collection import GetOptions, TouchOptions | ||
from couchbase.logic.collection import CollectionLogic | ||
from couchbase.pycbc_core import result | ||
from couchbase.result import ( | ||
ExistsResult, | ||
GetResult, | ||
MultiGetResult, | ||
MultiMutationResult, | ||
MutationResult, | ||
) | ||
|
||
from couchbase_helper.fake._datetime_hack import utcnow | ||
from couchbase_helper.fake.store import Store | ||
|
||
|
||
class Collection(CollectionLogic): | ||
def __init__(self, scope, name): | ||
super().__init__(scope, name) | ||
self._scope = scope | ||
self._name = name | ||
self._collection_name = f"{scope.name}-{name}" | ||
self._store = Store() | ||
|
||
def get(self, key, *opts, **kwargs): | ||
res = result() | ||
res.raw_result = self._store.get(self._collection_name, key) | ||
return GetResult(res) | ||
|
||
def get_multi(self, keys, *opts, **kwargs): | ||
return_exceptions = self._get_kwarg("return_exceptions", kwargs, True) | ||
res = result() | ||
docs = {} | ||
for key in keys: | ||
tmp_res = result() | ||
tmp_res.raw_result = self._store.get(self._collection_name, key) | ||
docs[key] = tmp_res | ||
del tmp_res | ||
|
||
res.raw_result = docs | ||
return MultiGetResult(res, return_exceptions=return_exceptions) | ||
|
||
def exists(self, key, *opts, **kwargs): | ||
res = result() | ||
res.raw_result = self._store.get(self._collection_name, key) | ||
return ExistsResult(res) | ||
|
||
def insert(self, key, value, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
res = result() | ||
try: | ||
self._store.insert(self._collection_name, key, value, expiry) | ||
res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
return MutationResult(res) | ||
|
||
def insert_multi(self, keys_and_docs, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
return_exceptions = self._get_kwarg("return_exceptions", kwargs, True) | ||
per_key_options = kwargs.get("per_key_options", {}) | ||
all_ok = True | ||
ops_res = {} | ||
for key, doc in keys_and_docs.items(): | ||
key_res = result() | ||
if key in per_key_options: | ||
doc_expiry = self._get_expiry(**per_key_options[key]) | ||
else: | ||
doc_expiry = expiry | ||
|
||
try: | ||
self._store.insert(self._collection_name, key, doc, doc_expiry) | ||
key_res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
all_ok = False | ||
key_res.raw_result = _exc | ||
|
||
ops_res[key] = key_res | ||
|
||
ops_res["all_okay"] = all_ok | ||
res = result() | ||
res.raw_result = ops_res | ||
return MultiMutationResult(res, return_exceptions) | ||
|
||
def upsert(self, key, value, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
res = result() | ||
try: | ||
self._store.upsert(self._collection_name, key, value, expiry) | ||
res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
return MutationResult(res) | ||
|
||
def upsert_multi(self, keys_and_docs, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
return_exceptions = self._get_kwarg("return_exceptions", kwargs, True) | ||
per_key_options = kwargs.get("per_key_options", {}) | ||
all_ok = True | ||
ops_res = {} | ||
for key, doc in keys_and_docs.items(): | ||
key_res = result() | ||
if key in per_key_options: | ||
doc_expiry = self._get_expiry(**per_key_options[key]) | ||
else: | ||
doc_expiry = expiry | ||
|
||
try: | ||
self._store.upsert(self._collection_name, key, doc, doc_expiry) | ||
key_res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
all_ok = False | ||
key_res.raw_result = _exc | ||
|
||
ops_res[key] = key_res | ||
|
||
ops_res["all_okay"] = all_ok | ||
res = result() | ||
res.raw_result = ops_res | ||
return MultiMutationResult(res, return_exceptions) | ||
|
||
def replace(self, key, value, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
res = result() | ||
try: | ||
self._store.replace(self._collection_name, key, value, expiry) | ||
res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
return MutationResult(res) | ||
|
||
def remove(self, key, *opts, **kwargs): | ||
res = result() | ||
try: | ||
self._store.remove(self._collection_name, key) | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
res.raw_result = {"cas": time_ns(), "key": key} | ||
return MutationResult(res) | ||
|
||
def remove_multi(self, keys, *opts, **kwargs): | ||
return_exceptions = self._get_kwarg("return_exceptions", kwargs, True) | ||
all_ok = True | ||
ops_res = {} | ||
for key in keys: | ||
key_res = result() | ||
try: | ||
key_kwargs = kwargs.get("per_key_options", {}).get(key, kwargs) | ||
self.remove(key, *opts, **key_kwargs) | ||
key_res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
all_ok = False | ||
key_res.raw_result = _exc | ||
|
||
ops_res[key] = key_res | ||
|
||
ops_res["all_okay"] = all_ok | ||
res = result() | ||
res.raw_result = ops_res | ||
return MultiMutationResult(res, return_exceptions) | ||
|
||
def touch(self, key, *opts, **kwargs): | ||
expiry = self._get_expiry(**kwargs) | ||
res = result() | ||
try: | ||
self._store.touch(self._collection_name, key, expiry) | ||
res.raw_result = {"cas": time_ns(), "key": key} | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
return MutationResult(res) | ||
|
||
def get_and_touch(self, key, **kwargs): | ||
try: | ||
self.touch(key, TouchOptions(), **kwargs) | ||
return self.get(key, GetOptions(), **kwargs) | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
def get_and_lock(self, key, **kwargs): | ||
lock_time = self._get_kwarg("lock_time", kwargs) | ||
try: | ||
res = self.get(key, GetOptions(), **kwargs) | ||
self._store.lock(self._collection_name, key, lock_time) | ||
return res | ||
except Exception as _exc: | ||
raise _exc | ||
|
||
def unlock(self, key, cas, *opts, **kwargs): | ||
self._store.unlock(self._collection_name, key) | ||
|
||
def lookup_in(self, key, spec, *opts, **kwargs): | ||
document = self.get(key, *opts, **kwargs) | ||
value = document.value | ||
ret = {} | ||
for s in spec: | ||
if s in value: | ||
ret[s] = value[s] | ||
|
||
return ret | ||
|
||
@staticmethod | ||
def default_name(): | ||
return "_default" | ||
|
||
def _get_expiry(self, **kwargs) -> float: | ||
expiry_delta = self._get_kwarg("expiry", kwargs) | ||
if expiry_delta is not None: | ||
if isinstance(expiry_delta, int): | ||
expiry_delta = timedelta(seconds=expiry_delta) | ||
dt_exp = utcnow() + expiry_delta | ||
return dt_exp.timestamp() | ||
|
||
return 0 | ||
|
||
@staticmethod | ||
def _get_kwarg(arg, kwargs, default=None): | ||
return kwargs.get(arg, default) | ||
|
||
@property | ||
def store(self): | ||
return self._store |
Empty file.
Oops, something went wrong.