6 Commits

Author SHA1 Message Date
Jack Case
cbabd5da3c testing top offenders query 2025-11-26 21:43:18 +00:00
Jack Case
283e9c6b7f Merge branch 'feat_testing' into feat_domain_preload 2025-11-26 21:15:53 +00:00
Jack Case
1cb9ae8606 writing some unit tests 2025-11-26 20:44:02 +00:00
Jack Case
6a1837762e move testing-related files to test submodule
create scripts to load and dump the test database to and from SQL files
2025-11-26 16:19:14 +00:00
Jack Case
fcc5d9d7bc WIP: wrote a query to get domains ordered by number of reported paths 2025-11-24 18:59:22 +00:00
Jack Case
a36b6e9865 actually send the email 2025-11-15 16:04:45 +00:00
10 changed files with 96 additions and 5 deletions

3
.gitignore vendored
View File

@@ -214,4 +214,7 @@ __marimo__/
# Streamlit
.streamlit/secrets.toml
slopserver/server_config.env
test_db.sqlite

View File

@@ -1,7 +1,7 @@
from collections.abc import Iterable
from datetime import datetime
from urllib.parse import ParseResult
from sqlalchemy import select
from sqlalchemy import select, func
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session
from slopserver.models import Domain, Path, User, Report
@@ -11,6 +11,13 @@ def select_slop(urls: list[ParseResult], engine: Engine) -> Iterable[Domain]:
with Session(engine) as session:
rows = session.scalars(query).all()
return rows
def top_offenders(engine: Engine, limit: int|None = None) -> Iterable[Domain]:
query = select(Domain.domain_name, func.count(Path.id)).join(Path).group_by(Domain.id).order_by(func.count(Path.id).desc())
if limit: query = query.limit(limit)
with Session(engine) as session:
top_offenders = session.execute(query).all()
return top_offenders
def insert_slop(urls: list[ParseResult], engine: Engine, user: User | None = None):
domain_dict: dict[str. set[str]] = dict()

View File

@@ -34,6 +34,7 @@ from slopserver.settings import settings
from slopserver.models import Domain, Path, User
from slopserver.models import SlopReport, SignupForm, altcha_validator
from slopserver.db import select_slop, insert_slop, get_user, create_user, verify_user_email
from slopserver.email import generate_verification_email, send_email
app = FastAPI()
@@ -150,9 +151,10 @@ def signup_form(form_data: Annotated[SignupForm, Form()]):
# send verification email
# create a jwt encoding the username and a time limit to be the verification URL
token = generate_verification_token(form_data.email)
return token
email_html = generate_verification_email(settings.api_base + "verify/?token=" + token)
status = send_email(form_data.email, "Slop Farmer Email Verification", email_html)
return status
@app.get("/verify")
def verify_email(token: Annotated[str, AfterValidator(verify_verification_token)]):

View File

@@ -2,11 +2,12 @@ from pydantic_settings import BaseSettings
class ServerSettings(BaseSettings):
db_url: str = "sqlite+pysqlite:///test_db.sqlite"
db_url: str = "sqlite+pysqlite:///slopserver/test/test_db.sqlite"
token_secret: str = "5bcc778a96b090c3ac1d587bb694a060eaf7bdb5832365f91d5078faf1fff210"
altcha_secret: str = "0460de065912d0292df1e7422a5ed2dc362ed56d6bab64fe50b89957463061f3"
resend_token: str = "re_NXpjzbqR_KgAbu72PKjYHcquX24WvnN3i"
sender_email: str = "slopfarmer@jack-case.pro"
api_base: str = "api.slopfarmer.jack-case.pro/"
settings = ServerSettings()

View File

View File

@@ -0,0 +1,5 @@
#!/bin/bash
test_dir=slopserver/test
sqlite3 $test_dir/test_db.sqlite .dump > $1

View File

@@ -0,0 +1,5 @@
#!/bin/bash
test_dir=slopserver/test
cat $test_dir/test_db.sql | sqlite3 $test_dir/test_db.sqlite

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
from fastapi.testclient import TestClient
from slopserver.server import app as slopserver_app
client = TestClient(slopserver_app)

View File

@@ -0,0 +1,18 @@
from slopserver.db import *
from slopserver.models import *
from slopserver.settings import settings
from sqlalchemy import create_engine
import unittest
class TestDBFuncs(unittest.TestCase):
test_db_url = settings.db_url
def setUp(self):
self.engine = create_engine(self.test_db_url)
def test_get_top_offenders(self):
items = top_offenders(self.engine)
print(items)
self.assertEqual(items[0][0], "moogle.com")

View File

@@ -0,0 +1,45 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE domain (
id INTEGER NOT NULL,
domain_name VARCHAR NOT NULL,
CONSTRAINT pk_domain PRIMARY KEY (id)
);
INSERT INTO domain VALUES(1,'google.com');
INSERT INTO domain VALUES(2,'moogle.com');
CREATE TABLE user (
id INTEGER NOT NULL,
email VARCHAR NOT NULL,
password_hash VARCHAR NOT NULL,
email_verified BOOLEAN NOT NULL,
CONSTRAINT pk_user PRIMARY KEY (id)
);
INSERT INTO user VALUES(1,'alphauser01','$argon2id$v=19$m=65536,t=3,p=4$3z7uxa3NHl/dKG07RGEvBA$0NOBftJpP+HiR7wfgdwBk2UR9F12YBjrqeqLSyDl47o','True');
CREATE TABLE path (
id INTEGER NOT NULL,
path VARCHAR NOT NULL,
domain_id INTEGER,
CONSTRAINT pk_path PRIMARY KEY (id),
CONSTRAINT fk_path_domain_id_domain FOREIGN KEY(domain_id) REFERENCES domain (id)
);
INSERT INTO path VALUES(1,'/',0);
INSERT INTO path VALUES(2,'/path1',0);
INSERT INTO path VALUES(3,'/path2',1);
INSERT INTO path VALUES(4,'/path3/a',1);
INSERT INTO path VALUES(5,'/path4',1);
INSERT INTO path VALUES(6,'/path2',2);
INSERT INTO path VALUES(7,'/',2);
INSERT INTO path VALUES(8,'/path3',2);
INSERT INTO path VALUES(9,'/path4',2);
CREATE TABLE report (
path_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
timestamp DATETIME,
CONSTRAINT pk_report PRIMARY KEY (path_id, user_id),
CONSTRAINT fk_report_path_id_path FOREIGN KEY(path_id) REFERENCES path (id),
CONSTRAINT fk_report_user_id_user FOREIGN KEY(user_id) REFERENCES user (id)
);
INSERT INTO report VALUES(2,1,'11-26-2025');
CREATE UNIQUE INDEX ix_domain_domain_name ON domain (domain_name);
CREATE UNIQUE INDEX ix_user_email ON user (email);
COMMIT;