initial tests for frant and back end
This commit is contained in:
parent
b4428e7048
commit
c26c2bb7e5
|
|
@ -0,0 +1,3 @@
|
||||||
|
# backend/requirements-dev.txt
|
||||||
|
pytest
|
||||||
|
pytest-dotenv # loads .env before running tests (optional)
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
# backend/tests/test_api.py
|
||||||
|
"""
|
||||||
|
Simple smoke–test 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 don’t 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
|
||||||
|
|
@ -37,6 +37,8 @@ dependencies:
|
||||||
supabase_flutter: ^2.9.1
|
supabase_flutter: ^2.9.1
|
||||||
flutter_dotenv: ^5.2.1
|
flutter_dotenv: ^5.2.1
|
||||||
gotrue: ^2.13.0
|
gotrue: ^2.13.0
|
||||||
|
http: ^1.2.0
|
||||||
|
dart_jsonwebtoken: ^2.12.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue