Add integration and unit tests for asset loading and content type determination

- Created `AssetLoadingIntegrationTest` to verify the loading of game assets in the correct order, including JavaScript, CSS, and sound files.
- Implemented tests to ensure proper handling of asset paths, security constraints, and response headers.
- Added `StaticFilesControllerUnitTest` to test the content type determination logic for various file extensions, ensuring case insensitivity and handling of multiple dots in filenames.
This commit is contained in:
Z. Cliffe Schreuders
2025-11-21 15:27:54 +00:00
parent 3acaa62c26
commit 9ac86a2c8d
15 changed files with 4966 additions and 374 deletions

View File

@@ -0,0 +1,202 @@
require 'test_helper'
module BreakEscape
class StaticFilesControllerTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
# CSS file serving tests
test 'CSS: should serve files with correct MIME type' do
get '/break_escape/css/hud.css'
assert_response :success
assert_equal 'text/css', response.content_type
end
test 'CSS: should serve with inline disposition' do
get '/break_escape/css/hud.css'
assert_response :success
assert_match /inline/, response.headers['Content-Disposition']
end
test 'CSS: should return 404 for non-existent files' do
get '/break_escape/css/non-existent.css'
assert_response :not_found
end
# JavaScript file serving tests
test 'JS: should serve files with correct MIME type' do
get '/break_escape/js/main.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'JS: should serve nested files' do
get '/break_escape/js/utils/constants.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'JS: should serve core game module' do
get '/break_escape/js/core/game.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'JS: should return 404 for non-existent files' do
get '/break_escape/js/non-existent.js'
assert_response :not_found
end
# Asset file serving tests
test 'Assets: should serve audio with correct MIME type' do
get '/break_escape/assets/sounds/lockpick_binding.mp3'
assert_response :success
assert_equal 'audio/mpeg', response.content_type
end
test 'Assets: should serve PNG tiles with correct MIME type' do
get '/break_escape/assets/tiles/door_32.png'
assert_response :success
assert_equal 'image/png', response.content_type
end
test 'Assets: should serve nested files' do
get '/break_escape/assets/tiles/door_32.png'
assert_response :success
assert_equal 'image/png', response.content_type
end
test 'Assets: should return 404 for non-existent files' do
get '/break_escape/assets/sounds/non-existent.mp3'
assert_response :not_found
end
# HTML test page serving tests
test 'HTML: should serve test files with correct MIME type' do
get '/break_escape/test-assets.html'
assert_response :success
assert_equal 'text/html', response.content_type
end
test 'HTML: should return 404 for non-existent files' do
get '/break_escape/non-existent.html'
assert_response :not_found
end
# Route parameter capture tests
test 'Routes: should capture full filename with extension' do
get '/break_escape/css/hud.css'
assert_response :success
end
test 'Routes: should capture complex paths with segments' do
get '/break_escape/js/utils/constants.js'
assert_response :success
end
test 'Routes: should handle files with multiple dots' do
get '/break_escape/assets/tiles/door_sheet_32.png'
assert_response :success
assert_equal 'image/png', response.content_type
end
# Security tests - directory traversal
test 'Security: should prevent directory traversal in CSS' do
get '/break_escape/css/../../config/database.yml'
assert_response :not_found
end
test 'Security: should prevent directory traversal in JS' do
get '/break_escape/js/../../config/secrets.yml'
assert_response :not_found
end
test 'Security: should prevent directory traversal in assets' do
get '/break_escape/assets/../../config/database.yml'
assert_response :not_found
end
# Phaser asset configuration
test 'Phaser: main JS imports GAME_CONFIG' do
get '/break_escape/js/main.js'
assert_response :success
assert_includes response.body, 'GAME_CONFIG'
end
test 'Phaser: constants define GAME_CONFIG with baseURL' do
get '/break_escape/js/utils/constants.js'
assert_response :success
content = response.body
assert_includes content, 'GAME_CONFIG'
assert_includes content, 'baseURL'
assert_includes content, '/break_escape/assets'
end
test 'Phaser: game.js has asset references without prefix' do
get '/break_escape/js/core/game.js'
assert_response :success
content = response.body
assert_includes content, 'this.load.audio'
assert content.include?('sounds/'), "Should reference sounds without assets/ prefix"
end
# File integrity tests
test 'Integrity: CSS files are non-empty' do
get '/break_escape/css/hud.css'
assert_response :success
assert response.body.length > 0
end
test 'Integrity: JavaScript files are non-empty' do
get '/break_escape/js/main.js'
assert_response :success
assert response.body.length > 0
end
test 'Integrity: audio files are non-empty' do
get '/break_escape/assets/sounds/lockpick_binding.mp3'
assert_response :success
assert response.body.length > 0
end
test 'Integrity: image files are non-empty' do
get '/break_escape/assets/tiles/door_32.png'
assert_response :success
assert response.body.length > 0
end
# Response header tests
test 'Headers: should include Cache-Control' do
get '/break_escape/css/hud.css'
assert_response :success
assert response.headers['Cache-Control']
end
test 'Headers: should set Content-Disposition to inline' do
get '/break_escape/css/hud.css'
assert_response :success
assert_match /inline/, response.headers['Content-Disposition']
end
test 'Headers: should include Content-Length' do
get '/break_escape/css/hud.css'
assert_response :success
assert response.headers['Content-Length']
end
# Minigame asset loading tests
test 'Minigame: should serve lockpicking script' do
get '/break_escape/js/minigames/lockpicking/lockpicking-game-phaser.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'Minigame: should serve lockpicking sounds' do
sounds = ['lockpick_binding.mp3', 'lockpick_click.mp3', 'lockpick_success.mp3']
sounds.each do |sound|
get "/break_escape/assets/sounds/#{sound}"
assert_response :success, "Failed to serve #{sound}"
assert_equal 'audio/mpeg', response.content_type
end
end
end
end

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,142 @@
require 'test_helper'
module BreakEscape
class AssetLoadingIntegrationTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
test 'should load all required game files in correct order' do
get '/break_escape/js/main.js'
assert_response :success
assert_includes response.body, 'GAME_CONFIG'
end
test 'should load GAME_CONFIG with proper baseURL' do
get '/break_escape/js/utils/constants.js'
assert_response :success
content = response.body
assert_match /export const GAME_CONFIG/, content
assert_match /baseURL/, content
assert_match /\/break_escape\/assets/, content
end
test 'CSS files should be accessible from main game' do
get '/break_escape/css/hud.css'
assert_response :success
assert_equal 'text/css', response.content_type
end
test 'should serve game core with asset references' do
get '/break_escape/js/core/game.js'
assert_response :success
content = response.body
assert_includes content, 'preload'
assert_includes content, 'this.load'
end
test 'should serve sound manager module' do
get '/break_escape/js/systems/sound-manager.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'should serve minigame starters' do
get '/break_escape/js/systems/minigame-starters.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
end
test 'should serve lockpicking minigame' do
get '/break_escape/js/minigames/lockpicking/lockpicking-game-phaser.js'
assert_response :success
assert_equal 'application/javascript', response.content_type
content = response.body
assert_match /baseURL/, content
end
test 'complete asset loading path for lockpicking' do
get '/break_escape/js/minigames/lockpicking/lockpicking-game-phaser.js'
assert_response :success
lockpick_sounds = [
'lockpick_binding.mp3',
'lockpick_click.mp3',
'lockpick_overtension.mp3',
'lockpick_reset.mp3',
'lockpick_set.mp3',
'lockpick_success.mp3',
'lockpick_tension.mp3',
'lockpick_wrong.mp3'
]
lockpick_sounds.each do |sound|
get "/break_escape/assets/sounds/#{sound}"
assert_response :success, "Lockpick sound not found: #{sound}"
assert_equal 'audio/mpeg', response.content_type
end
get '/break_escape/assets/tiles/door_32.png'
assert_response :success
assert_equal 'image/png', response.content_type
end
test 'route constraints correctly capture file extensions' do
files_to_test = [
'/break_escape/css/hud.css',
'/break_escape/js/main.js',
'/break_escape/assets/tiles/door_32.png',
'/break_escape/assets/sounds/lockpick_binding.mp3'
]
files_to_test.each do |file_path|
get file_path
assert_response :success, "Failed to load file with extension: #{file_path}"
end
end
test 'baseURL prevents duplicate asset paths' do
get '/break_escape/js/utils/constants.js'
assert_response :success
content = response.body
assert !content.include?('assets/assets'), "Should not have duplicate assets/ prefix"
end
test 'asset paths work without assets prefix in load calls' do
get '/break_escape/js/core/game.js'
assert_response :success
content = response.body
assert_match /this\.load\.audio\(['"][\w_]+['"],\s*['"]sounds\//, content
end
test 'security: cannot access files outside break_escape directory' do
get '/break_escape/css/../../config/secrets.yml'
assert_response :not_found
get '/break_escape/js/../../config/database.yml'
assert_response :not_found
get '/break_escape/assets/../../config/secrets.yml'
assert_response :not_found
end
test 'all response headers are correct' do
[
'/break_escape/css/hud.css',
'/break_escape/js/main.js',
'/break_escape/assets/sounds/lockpick_binding.mp3'
].each do |path|
get path
assert_response :success
assert response.headers['Content-Type'], "Missing Content-Type for #{path}"
assert_match /inline/, response.headers['Content-Disposition']
assert response.headers['Content-Length']
end
end
test 'test asset page loads correctly' do
get '/break_escape/test-assets.html'
assert_response :success
assert_equal 'text/html', response.content_type
assert_includes response.body, 'Asset Loading Test'
end
end
end

View File

@@ -0,0 +1,106 @@
require 'test_helper'
module BreakEscape
class StaticFilesControllerUnitTest < ActiveSupport::TestCase
# Test the content type determination logic in isolation
setup do
@controller = StaticFilesController.new
end
test 'determines CSS content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.css')
assert_equal 'text/css', content_type
end
test 'determines JavaScript content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.js')
assert_equal 'application/javascript', content_type
end
test 'determines HTML content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.html')
assert_equal 'text/html', content_type
end
test 'determines JSON content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.json')
assert_equal 'application/json', content_type
end
test 'determines PNG content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.png')
assert_equal 'image/png', content_type
end
test 'determines JPEG content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.jpg')
assert_equal 'image/jpeg', content_type
end
test 'determines GIF content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.gif')
assert_equal 'image/gif', content_type
end
test 'determines SVG content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.svg')
assert_equal 'image/svg+xml', content_type
end
test 'determines MP3 content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.mp3')
assert_equal 'audio/mpeg', content_type
end
test 'determines WAV content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.wav')
assert_equal 'audio/wav', content_type
end
test 'determines OGG content type' do
content_type = @controller.send(:determine_content_type, '/path/to/file.ogg')
assert_equal 'audio/ogg', content_type
end
test 'determines WOFF font content type' do
content_type = @controller.send(:determine_content_type, '/path/to/font.woff')
assert_equal 'font/woff', content_type
end
test 'determines WOFF2 font content type' do
content_type = @controller.send(:determine_content_type, '/path/to/font.woff2')
assert_equal 'font/woff2', content_type
end
test 'determines TTF font content type' do
content_type = @controller.send(:determine_content_type, '/path/to/font.ttf')
assert_equal 'font/ttf', content_type
end
test 'is case insensitive' do
content_type = @controller.send(:determine_content_type, '/path/to/FILE.CSS')
assert_equal 'text/css', content_type
content_type = @controller.send(:determine_content_type, '/path/to/FILE.JS')
assert_equal 'application/javascript', content_type
content_type = @controller.send(:determine_content_type, '/path/to/FILE.PNG')
assert_equal 'image/png', content_type
end
test 'handles multiple dots in filename' do
# Files like door_sheet_32.png should work
content_type = @controller.send(:determine_content_type, '/path/to/door_sheet_32.png')
assert_equal 'image/png', content_type
content_type = @controller.send(:determine_content_type, '/path/to/lockpick_binding.mp3')
assert_equal 'audio/mpeg', content_type
end
test 'returns octet-stream for unknown extensions' do
content_type = @controller.send(:determine_content_type, '/path/to/file.unknown')
assert_equal 'application/octet-stream', content_type
end
end
end