Renaming project to json-api for clarity sake

This commit is contained in:
shockrah
2021-01-24 13:34:17 -08:00
parent 84c865e194
commit b67bb6105f
52 changed files with 83 additions and 2 deletions

2
json-api/client-tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
bin/
lib/

View File

@@ -0,0 +1 @@
from . import web

View File

@@ -0,0 +1,220 @@
import time
import subprocess, os, sys
import json, requests
from web import http
class Worker:
def __init__(self, domain: str, create_admin: bool):
'''
@opt:base = base url string
@opt:create_admin = creates admin account directly off cargo -c <NAME>
@opt:admin = optionally pass in dictionary with some admin credentials
potentially from another instance to run multiple tests at once
'''
self.domain = domain
self.requests = {}
self.responses = {}
self.body_logs = {}
self.jwt = None # never gets assigned until /login is hit
self.basic_creds = self.__create_admin()
self.id = self.basic_creds['id']
self.secret = self.basic_creds['secret']
def __create_admin(self):
# /home/$user/.cargo/bin/cargo <- normally
# NOTE: the above path is prolly some awful shit on ci pipeline
CARGO_BIN = os.getenv('CARGO_BIN')
proc = subprocess.run(f'cargo run --release -- -c dev-test'.split(), text=True, capture_output=True)
try:
return json.loads(proc.stdout)
except:
import sys
print('TESTBOT UNABLE TO LOAD JSON DATA FROM -c flag', file=sys.stderr)
exit(1)
def auth(self, auth_opt: str):
if auth_opt == 'basic':
return self.basic_creds
else:
return {'id': self.id, 'jwt': auth_opt}
def _append_auth(self, opts: dict, auth: str):
'''
Default auth fallback type is jwt because that's they only other type of auth
FC cares about really
@param opts: Dictionary of parameters to pass to the endpoint
@auth: Denotes if we use basic auth or jwt if its not 'basic'
'''
if type(auth) == str:
opts['id'] = self.id
if auth == 'basic':
opts['secret'] = self.secret
else:
opts['jwt'] = auth
return opts
else:
# if its not a string we don't add anything in
return opts
def logs(self):
ids = sorted(self.requests.keys()) # shared keys in requests/responses
for key in ids:
self.responses[key].log()
# Logg the provided data to ensure that _it_ wasn't the cause for error
resp = self.responses[key]
if resp.code != resp.expected:
opts = self.requests[key].params
print(f'\tParams: {opts}')
if self.body_logs[key] is True:
print(f'\tBody: {self.responses[key].body}')
def request(self, method: str, path: str, auth: str, opts: dict, expectation: int, show_body=False):
assert(path[0] == '/')
# First make sure we add in the correct auth params that are requested
opts = self._append_auth(opts, auth)
# Build the request and store it in our structure
url = self.domain + path
req = http.Request(method, url, opts)
r_id = time.time()
resp = req.make(expectation)
# update log trackers
self.requests[r_id] = req
self.responses[r_id] = resp
self.body_logs[r_id] = show_body
return r_id
def run(worker: Worker):
VOICE_CHAN = 1
TEXT_CHAN = 2
# Basically every test requires a jwt to be passed in so we grab that here
# Should this fail so should nearly every other test from this point
req_login = worker.request('post', '/login', 'basic',{}, 200)
jwt = worker.responses[req_login].json()['jwt']
new_channel_name = time.time()
channel_tests = [
# sanity check
{'init': ['get', '/channels/list', {}], 'auth': None, 'hope': 401},
{'init': ['post', '/channels/list', {}], 'auth': jwt, 'hope': 404},
{'init': ['post', '/channels/create', {'name': str(new_channel_name), 'kind': TEXT_CHAN, 'description': 'asdf'}], 'auth': jwt, 'hope': 200},
# Just a regular test no saving for this one
{'init': ['post', '/channels/create', {'name': str(new_channel_name+1), 'kind': TEXT_CHAN,}], 'auth': jwt, 'hope': 200},
{'init': ['post', '/channels/create', {}], 'auth': jwt, 'hope': 400},
{'init': ['post', '/channels/create', {'name': 123, 'kind': 'adsf'}], 'auth': jwt, 'hope': 400},
# save this and compare its results to the previous
{'init': ['get', '/channels/list', {}], 'auth': jwt, 'hope': 200},
{'init': ['get', '/channels/list', {'random-param': 123}], 'auth': jwt, 'hope': 200},
]
for test in channel_tests:
method, path, opts = test['init']
auth = test['auth']
hope = test['hope']
worker.request(method, path, auth, opts, hope)
msg_chan_name = time.time()
_id = worker.request('post', '/channels/create', jwt, {
'name': str(msg_chan_name),
'kind': TEXT_CHAN,
}, 200)
chan_d = worker.responses[_id].json()
message_tests = [
# bs message spam
{'init': ['post', '/message/send', {'channel': chan_d['id'], 'content': 'bs content'}], 'auth': jwt, 'hope': 200},
{'init': ['post', '/message/send', {'channel': chan_d['id'], 'content': 'bs content'}], 'auth': jwt, 'hope': 200},
{'init': ['post', '/message/send', {'channel': chan_d['id'], 'content': 'bs content'}], 'auth': jwt, 'hope': 200},
{'init': ['post', '/message/send', {'channel': chan_d['id'], 'content': 'bs content'}], 'auth': jwt, 'hope': 200},
# can we get them back tho?
{
'init': [
'get', '/message/get_range', {'channel': chan_d['id'], 'start-time': int(msg_chan_name-10), 'end-time': int(msg_chan_name + 10)}
],
'auth': jwt, 'hope': 200
},
{
'init': [
'get', '/message/get_range', {'channel': chan_d['id'], 'end-time': int(msg_chan_name)}
],
'auth': jwt, 'hope': 400
},
{
'init': [
'get', '/message/get_range', {'channel': chan_d['id'], 'start-time': int(msg_chan_name), 'end-time': int(msg_chan_name)}
],
'auth': jwt, 'hope': 400
},
{
'init': [
'get', '/message/get_range', {'channel': chan_d['id'], 'end-time': int(msg_chan_name), 'start-time': int(msg_chan_name)}
],
'auth': jwt, 'hope': 400
},
# two tests that follow the rules
{
'init': [
'get', '/message/from_id', {'start': 1, 'channel': 3}
],
'auth': jwt, 'hope': 200, 'body': True
},
{
'init': [
'get', '/message/from_id', {'start':1, 'channel':3, 'limit':2}
],
'auth': jwt, 'hope': 200, 'body': True
},
# tests that don't follow the api's rules
{
# channel doesn't exist so a 404 seems to be inorder
'init': [
'get', '/message/from_id', {'start': 1, 'channel':9}
],
'auth': jwt, 'hope': 404
},
{
'init': [
# good channel but id is tooo high
'get', '/message/from_id', {'start': 5, 'channel':3}
],
'auth': jwt, 'hope': 404
},
]
for test in message_tests:
method, path, opts = test['init']
auth = test['auth']
hope = test['hope']
if 'body' in test:
worker.request(method, path, auth, opts, hope, show_body=test['body'])
else:
worker.request(method, path, auth, opts, hope)
worker.logs()
if __name__ == '__main__':
worker = Worker('http://localhost:8888', create_admin=True)
run(worker)

View File

@@ -0,0 +1,8 @@
home = /usr
implementation = CPython
version_info = 3.9.1.final.0
virtualenv = 20.2.2
include-system-site-packages = false
base-prefix = /usr
base-exec-prefix = /usr
base-executable = /usr/bin/python3

View File

@@ -0,0 +1,5 @@
certifi==2020.12.5
chardet==4.0.0
idna==2.10
requests==2.25.1
urllib3==1.26.2

View File

@@ -0,0 +1 @@
from . import http

View File

@@ -0,0 +1,126 @@
import sys
import requests
import time
import json
class RequestError(Exception):
pass
class Response:
'''
Response is wrapper for reading + extracting information we care about
Primarily created by Requests that get `make`'d.
'''
def __init__(self, method: str, url: str, body: str, code: int, expected: int, out=sys.stdout, color=True,
truncate_long_body=True):
self.method = method
self.url = url
self.body = body #typically a string before parsing anything
self.code = code #u16
self.expected = expected #u16
self.out = out # file handle to write to normally sys.stdout
self.color = color # bool telling if log should color anything (on by default)
self.truncate_long_body = truncate_long_body
def _color(self, cc, string):
nc = '\033[0m'
return f'{cc}{string}{nc}'
def _color_failing(self, string):
red = '\033[1;31m'
return self._color(red, string)
def _color_passing(self, string):
green = '\033[1;32m'
return self._color(green, string)
def __write_msg(self, s):
# mega dumb wrapper to reduce visual noise i think
if (len(s) == 1 or len(s) == 0) is False: print(s, file=self.out)
def _log_body(self):
if self.truncate_long_body:
if len(self.body) > 80:
msg = self.body
while len(msg) > 80:
msg = msg[:len(msg)//2] + msg[len(msg)//2+1:]
msg = '.....'.join([msg[:40], msg[44]])
self.__write_msg(msg)
else:
self.__write_msg(f'\t{self.body}')
else:
self.__write_msg(f'\t{self.body}')
def log(self):
if self.code != self.expected:
msg = f'Failed {self.method.upper()} {self.url}\t{self.code} expected {self.expected}'
if self.color:
msg = self._color_failing(msg)
self.__write_msg(msg)
self._log_body()
else:
msg = f'Passing: {self.method} {self.url}'
if self.color:
msg = self._color_passing(msg)
self.__write_msg(msg)
def json(self):
'''
Force an interpretation of json from the body
NOTE: this method is rather obtuse and a massive afterthough so its usage
should be limited as much as possible
'''
try:
return json.loads(self.body)
except:
return {}
def __str__(self):
'''
Returns: str(Response) -> `code => response.bdoy`
'''
return f'{self.code} => {self.body}'
class Request:
def __init__(self, method: str, url: str, params: dict):
assert(method in ['get', 'post', 'delete'])
self.method = method
self.url = url
self.params = params
def _make_request(self, method: str, hope: int):
# Lower driver for actuall making the request we are looking for
method = method.lower()
params = json.dumps(self.params)
if method == 'get':
resp = requests.get(self.url, data=params)
return Response('get', self.url, resp.text, resp.status_code, hope)
elif method == 'post':
resp = requests.post(self.url, data=params)
return Response('post', self.url, resp.text, resp.status_code, hope)
elif method == 'delete':
resp = requests.delete(self.url, data=params)
return Response('delete', self.url, resp.text, resp.status_code, hope)
else:
raise RequestError('Invalid method passed')
return resp
def make(self, hope: int) -> Response:
'''
@param hope: int -> status code we hope to get back
@return Response -> Wrapper around server http response
'''
return self._make_request(self.method, hope)