import 'dart:io'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:test/test.dart'; void main() { final port = '8080'; final host = 'http://localhost:$port'; late Process p; setUp(() async { p = await Process.start( 'dart', ['run', 'bin/server.dart'], environment: {'PORT': port}, ); // Wait for server to start (give it some time to initialize) await Future.delayed(const Duration(seconds: 3)); }); tearDown(() async { p.kill(); // Wait for process to exit try { await p.exitCode.timeout(const Duration(seconds: 5), onTimeout: () => 0); } catch (_) {} }); group('Root endpoint', () { test('GET /api/v1/ returns Hello World', () async { final response = await http.get(Uri.parse('$host/api/v1/')); expect(response.statusCode, 200); expect(response.body, 'Hello, World!\n'); }); }); group('Echo endpoint', () { test('GET /api/v1/echo/ returns the message', () async { final response = await http.get(Uri.parse('$host/api/v1/echo/hello')); expect(response.statusCode, 200); expect(response.body, 'hello\n'); }); test('GET /api/v1/echo/ with special characters', () async { final response = await http.get( Uri.parse('$host/api/v1/echo/test%20message'), ); expect(response.statusCode, 200); expect(response.body, contains('test')); }); }); group('Album endpoints', () { test('GET /api/v1/album returns list of albums', () async { final response = await http.get(Uri.parse('$host/api/v1/album')); expect(response.statusCode, 200); expect(response.headers['content-type'], contains('application/json')); final albums = jsonDecode(response.body) as List; expect(albums, isA()); if (albums.isNotEmpty) { expect(albums.first, containsPair('id', isA())); expect(albums.first, containsPair('name', isA())); } }); test('GET /api/v1/album/ with invalid id returns 400', () async { final response = await http.get( Uri.parse('$host/api/v1/album/invalid_id'), ); expect(response.statusCode, 400); expect(response.body, contains('Invalid album id')); }); test('GET /api/v1/album/ with non-existent id returns 404', () async { final response = await http.get(Uri.parse('$host/api/v1/album/999999')); expect(response.statusCode, 404); expect(response.body, contains('Album not found')); }); test('GET /api/v1/album//photo with invalid id returns 400', () async { final response = await http.get( Uri.parse('$host/api/v1/album/invalid_id/photo'), ); expect(response.statusCode, 400); expect(response.body, contains('Invalid album id')); }); test('GET /api/v1/album//photo with valid id returns photos', () async { // First get the list to find a valid album id final listResponse = await http.get(Uri.parse('$host/api/v1/album')); expect(listResponse.statusCode, 200); final albums = jsonDecode(listResponse.body) as List; // Skip test if no albums exist if (albums.isEmpty) { return; } final album = albums.first as Map; final albumId = album['id']; final response = await http.get( Uri.parse('$host/api/v1/album/$albumId/photo'), ); expect(response.statusCode, 200); expect(response.headers['content-type'], contains('application/json')); final photos = jsonDecode(response.body) as List; expect(photos, isA()); }); }); group('Photo endpoints', () { test('GET /api/v1/photo/ with invalid id returns 400', () async { final response = await http.get(Uri.parse('$host/api/v1/photo/abc')); expect(response.statusCode, 400); expect(response.body, contains('Invalid photo id')); }); test('GET /api/v1/photo/ with non-existent id returns 404', () async { // Use a hash that won't match any file final response = await http.get(Uri.parse('$host/api/v1/photo/12345')); expect(response.statusCode, 404); expect(response.body, contains('Photo not found')); }); test('GET /api/v1/photo//file with invalid id returns 400', () async { final response = await http.get(Uri.parse('$host/api/v1/photo/abc/file')); expect(response.statusCode, 400); expect(response.body, contains('Invalid photo id')); }); test( 'GET /api/v1/photo//file with non-existent id returns 404', () async { // Use a hash that won't match any file final response = await http.get( Uri.parse('$host/api/v1/photo/12345/file'), ); expect(response.statusCode, 404); expect(response.body, contains('Photo not found')); }, ); test('GET /api/v1/photo//file with valid id returns file', () async { // First get a valid photo id from an album final albumResponse = await http.get(Uri.parse('$host/api/v1/album')); expect(albumResponse.statusCode, 200); final albums = jsonDecode(albumResponse.body) as List; // Skip test if no albums exist if (albums.isEmpty) { return; } final album = albums.first as Map; final albumId = album['id']; final photosResponse = await http.get( Uri.parse('$host/api/v1/album/$albumId/photo'), ); expect(photosResponse.statusCode, 200); final photos = jsonDecode(photosResponse.body) as List; // Skip test if no photos exist if (photos.isEmpty) { return; } final photo = photos.first as Map; final photoId = photo['id']; final response = await http.get( Uri.parse('$host/api/v1/photo/$photoId/file'), ); expect(response.statusCode, 200); expect(response.contentLength, greaterThan(0)); }); }); group('404 handling', () { test('Unknown route returns 404', () async { final response = await http.get(Uri.parse('$host/foobar')); expect(response.statusCode, 404); }); test('Unknown nested route returns 404', () async { final response = await http.get(Uri.parse('$host/api/v2/unknown')); expect(response.statusCode, 404); }); }); }