mts-the-architect's picture
Publish Gemma 4 classifier, experiment arms and inference documentation
d715249 verified
Raw History Blame Contribute Delete
3.76 kB
import concurrent.futures
import json
import tempfile
import threading
import unittest
import urllib.request
import urllib.error
from pathlib import Path
from http.server import ThreadingHTTPServer
from unittest.mock import patch
from api_access import AccessStore, AccessError
import serve_models
class AccessTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.store = AccessStore(Path(self.temp.name)/'access.db')
def tearDown(self):
self.temp.cleanup()
def test_approval_revoke_and_hashed_storage(self):
r = self.store.request('Test app', 'owner@example.test')
with self.assertRaises(AccessError) as e: self.store.authorize(r['api_key'])
self.assertEqual(e.exception.status, 403)
self.store.change(r['request_id'], 'approved', 2, 3)
self.store.authorize(r['api_key'])
self.store.change(r['request_id'], 'revoked')
with self.assertRaises(AccessError) as e: self.store.authorize(r['api_key'])
self.assertEqual(e.exception.status, 403)
self.assertNotIn(r['api_key'].encode(), self.store.path.read_bytes())
self.assertEqual(len(AccessStore(self.store.path).list()), 1)
def test_atomic_rate_limit(self):
def charge(_):
try: self.store.charge('shared', 2, 10, now=200000); return True
except AccessError: return False
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
self.assertEqual(sum(executor.map(charge, range(20))), 2)
self.store.charge('shared', 2, 10, now=200061)
def test_daily_reset(self):
self.store.charge('daily', 10, 1, now=200000)
with self.assertRaises(AccessError): self.store.charge('daily', 10, 1, now=200100)
self.store.charge('daily', 10, 1, now=259201)
def test_http_pending_approved_rate_limit_and_revocation(self):
serve_models.ACCESS = self.store
serve_models.ALLOW_ANONYMOUS = False
server = ThreadingHTTPServer(('127.0.0.1', 0), serve_models.Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True); thread.start()
def post(path, data, key=None):
headers = {'Content-Type':'application/json'}
if key: headers['Authorization'] = 'Bearer '+key
req = urllib.request.Request(f'http://127.0.0.1:{server.server_port}'+path,
data=json.dumps(data).encode(),headers=headers)
try: response = urllib.request.urlopen(req)
except urllib.error.HTTPError as error: response = error
with response: return response.status, json.load(response), response.headers
try:
self.assertEqual(post('/v1/emotions', {'text':'Hello'})[0],401)
status,r,_ = post('/v1/access-requests',{'app_name':'HTTP test','contact':'test@example.test'})
self.assertEqual(status,201)
self.assertEqual(post('/v1/emotions',{'text':'Hello'},r['api_key'])[0],403)
self.store.change(r['request_id'],'approved',2,2)
with patch.object(serve_models,'predict',return_value={'windows':[]}):
for _ in range(2): self.assertEqual(post('/v1/emotions',{'text':'Hello'},r['api_key'])[0],200)
status,_,headers = post('/v1/emotions',{'text':'Hello'},r['api_key'])
self.assertEqual(status,429); self.assertGreater(int(headers['Retry-After']),0)
self.store.change(r['request_id'],'revoked')
self.assertEqual(post('/v1/emotions',{'text':'Hello'},r['api_key'])[0],403)
finally:
server.shutdown(); server.server_close(); thread.join()
if __name__ == '__main__': unittest.main()