initial tests for frant and back end

This commit is contained in:
Mike Kell 2025-07-24 22:15:43 -04:00
parent b4428e7048
commit c26c2bb7e5
5 changed files with 199 additions and 30 deletions

View File

@ -0,0 +1,3 @@
# backend/requirements-dev.txt
pytest
pytest-dotenv # loads .env before running tests (optional)

93
backend/tests/test_api.py Normal file
View File

@ -0,0 +1,93 @@
# backend/tests/test_api.py
"""
Simple smoketest suite for the ComplyCore FastAPI backend.
Run with:
poetry install --with dev # or `pip install -r requirements-dev.txt`
pytest backend/tests # or simply `pytest` at repo root
"""
import os
from datetime import datetime, timedelta, timezone
import jwt # PyJWT
import pytest
from fastapi.testclient import TestClient
# ---- local import of your FastAPI app ---------------------------------------
from backend.main import app # adjusts if your main.py lives elsewhere
# -----------------------------------------------------------------------------
client = TestClient(app)
# --------------------------------------------------------------------------- #
# Helpers #
# --------------------------------------------------------------------------- #
def make_test_jwt(
user_id: str = "00000000-0000-0000-0000-000000000002",
email: str = "testuser@complycore.dev",
role: str = "authenticated",
) -> str:
"""
Craft a short-lived JWT signed with the same secret the API expects.
"""
secret = os.getenv("SUPABASE_JWT_SECRET", "NOT_SET")
if secret == "NOT_SET":
raise RuntimeError(
"SUPABASE_JWT_SECRET not loaded copy backend/.env.example → "
".env and set your secret before running the tests"
)
now = datetime.now(timezone.utc)
payload = {
"sub": user_id,
"aud": "authenticated",
"role": role,
"email": email,
"iat": int(now.timestamp()),
"exp": int((now + timedelta(minutes=10)).timestamp()),
"iss": "supabase",
"email_confirmed_at": now.isoformat(),
}
return jwt.encode(payload, secret, algorithm="HS256")
# --------------------------------------------------------------------------- #
# Tests #
# --------------------------------------------------------------------------- #
def test_healthz():
r = client.get("/healthz")
assert r.status_code == 200
assert r.json() == {"status": "ok"}
@pytest.mark.skipif(
os.getenv("CI") == "true",
reason="Requires network access to Supabase; skip on CI",
)
def test_supabase_check():
"""
Only a connectivity smoke test we dont assert DB/user values because
those differ per environment, we just ensure *something* comes back.
"""
r = client.get("/supabase-check")
assert r.status_code == 200
body = r.json()
assert body.get("status") == "connected"
assert "db" in body and "user" in body
def test_me_authorized():
token = make_test_jwt()
r = client.get("/me", headers={"Authorization": f"Bearer {token}"})
assert r.status_code == 200
body = r.json()
assert body["id"] == "00000000-0000-0000-0000-000000000002"
assert body["email"] == "testuser@complycore.dev"
assert body["role"] == "authenticated"
def test_me_unauthorized():
r = client.get("/me") # no token
assert r.status_code == 401

View File

@ -37,6 +37,8 @@ dependencies:
supabase_flutter: ^2.9.1
flutter_dotenv: ^5.2.1
gotrue: ^2.13.0
http: ^1.2.0
dart_jsonwebtoken: ^2.12.1
dev_dependencies:
flutter_test:

View File

@ -0,0 +1,101 @@
// frontend/complycore_flutter/test/api_smoke_test.dart
//
// Basic integration-style smoke tests that hit the running ComplyCore
// FastAPI backend from a Flutter test runner.
//
// Run with:
//
// flutter test --dart-define=SUPABASE_JWT_SECRET=your_secret_here
//
// while the backend is already up on http://localhost:8000.
//
// If you need a different host/port, override at the top of this file
// or pass --dart-define=API_BASE_URL=http://YOUR_HOST:PORT
//
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
/// Base URL for the API the tests will hit.
/// Override at runtime with:
/// flutter test --dart-define=API_BASE_URL=http://192.168.1.50:8000
const String _apiBaseUrl = const String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'http://localhost:8000',
);
/// Build a short-lived JWT signed with the same secret the backend expects.
String _buildJwt({
String userId = '00000000-0000-0000-0000-000000000002',
String email = 'testuser@complycore.dev',
String role = 'authenticated',
}) {
final secret = const String.fromEnvironment(
'SUPABASE_JWT_SECRET',
defaultValue: 'NOT_SET',
);
if (secret == 'NOT_SET') {
throw StateError(
'SUPABASE_JWT_SECRET not provided.\n'
'Run tests with:\n'
' flutter test --dart-define=SUPABASE_JWT_SECRET=your_secret_here',
);
}
final now = DateTime.now().toUtc();
final jwt = JWT({
'sub': userId,
'aud': 'authenticated',
'role': role,
'email': email,
'iat': (now.millisecondsSinceEpoch / 1000).floor(),
'exp': (now.add(const Duration(minutes: 10)).millisecondsSinceEpoch / 1000)
.floor(),
'iss': 'supabase',
'email_confirmed_at': now.toIso8601String(),
});
return jwt.sign(SecretKey(secret), algorithm: JWTAlgorithm.HS256);
}
void main() {
group('ComplyCore API smoke tests (Flutter)', () {
test('GET /healthz → 200 {"status":"ok"}', () async {
final res = await http.get(Uri.parse('$_apiBaseUrl/healthz'));
expect(res.statusCode, 200);
expect(jsonDecode(res.body)['status'], 'ok');
});
test(
'GET /supabase-check → 200, status=="connected"',
() async {
final res = await http.get(Uri.parse('$_apiBaseUrl/supabase-check'));
expect(res.statusCode, 200);
final body = jsonDecode(res.body);
expect(body['status'], 'connected');
},
skip: const bool.fromEnvironment('CI', defaultValue: false),
); // skip on CI
test('GET /me with valid JWT → 200, correct email', () async {
final token = _buildJwt();
final res = await http.get(
Uri.parse('$_apiBaseUrl/me'),
headers: {'Authorization': 'Bearer $token'},
);
expect(res.statusCode, 200);
final body = jsonDecode(res.body) as Map<String, dynamic>;
expect(body['email'], 'testuser@complycore.dev');
expect(body['role'], 'authenticated');
});
test('GET /me without token → 401/403', () async {
final res = await http.get(Uri.parse('$_apiBaseUrl/me'));
expect(res.statusCode, anyOf(401, 403));
});
});
}

View File

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:complycore_flutter/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}