new app store assets ready for relese

This commit is contained in:
Mark Gregory
2026-11-05 09:02:29 +00:00
parent db6c3488f1
commit 95bf13a129
35 changed files with 1516 additions and 506 deletions
+292 -57
View File
@@ -1,63 +1,298 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use App\Livewire\Admin\Cms;
use App\Livewire\SupportDesk;
use App\Models\User;
use App\Services\LicenseService;
use App\Livewire\SupportDesk;
use App\Livewire\Admin\Cms;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Livewire\Livewire;
class CommerceTest extends TestCase {
use RefreshDatabase;
protected function setUp(): void {parent::setUp();$this->seed();Notification::fake();config(['tarnax.paddle.webhook_secret'=>'test-secret','tarnax.paddle.price_id'=>'pri_test']);}
private function customer(array $attributes=[]): User {return User::factory()->create(['account_token'=>(string)Str::uuid(),...$attributes]);}
private function event(User $user,string $id='evt_1'): array {return ['event_id'=>$id,'event_type'=>'transaction.completed','occurred_at'=>now()->toISOString(),'data'=>['id'=>'txn_1','status'=>'completed','custom_data'=>['account_token'=>$user->account_token],'items'=>[['price'=>['id'=>'pri_test'],'quantity'=>1]],'details'=>['totals'=>['grand_total'=>'4900']],'currency_code'=>'USD']];}
private function deliver(array $event,?int $timestamp=null,?string $signature=null) {
$body=json_encode($event);$ts=$timestamp??time();$sig=$signature??hash_hmac('sha256',$ts.':'.$body,'test-secret');
return $this->call('POST','/api/webhooks/paddle',[],[],[],['CONTENT_TYPE'=>'application/json','HTTP_ACCEPT'=>'application/json','HTTP_PADDLE_SIGNATURE'=>'ts='.$ts.';h1='.$sig],$body);
}
private function license(User $user): object {$this->deliver($this->event($user))->assertOk();return DB::table('licenses')->first();}
public function test_public_pages_and_authentication_views_render(): void {foreach(['/','/releases','/pages/privacy','/pages/terms','/pages/features','/login','/register','/forgot-password'] as $url){$this->get($url)->assertOk();}$this->get('/account')->assertRedirect('/login');}
public function test_registration_cannot_grant_administration(): void {$this->post('/register',['name'=>'Customer','email'=>'new@example.com','password'=>'StrongPass123!','password_confirmation'=>'StrongPass123!','is_admin'=>true])->assertRedirect();$this->assertFalse(User::where('email','new@example.com')->first()->is_admin);}
public function test_verified_payment_issues_only_one_license_on_retries(): void {$user=$this->customer();$event=$this->event($user);$this->deliver($event)->assertOk();$this->deliver($event)->assertOk();$event['event_id']='evt_2';$this->deliver($event)->assertOk();$this->assertDatabaseCount('purchases',1);$this->assertDatabaseCount('licenses',1);}
public function test_invalid_signature_and_stale_replay_never_grant_licenses(): void {$user=$this->customer();$this->deliver($this->event($user),null,'invalid')->assertUnauthorized();$this->deliver($this->event($user),time()-600)->assertUnauthorized();$this->assertDatabaseCount('licenses',0);}
public function test_wrong_product_does_not_grant_license(): void {$event=$this->event($this->customer());$event['data']['items'][0]['price']['id']='pri_other';$this->deliver($event)->assertOk();$this->assertDatabaseCount('licenses',0);}
public function test_unknown_customer_rolls_back_event_for_retry(): void {$event=$this->event($this->customer());$event['data']['custom_data']['account_token']=(string)Str::uuid();$this->deliver($event)->assertUnprocessable();$this->assertDatabaseCount('webhook_events',0);}
public function test_activation_limits_validation_and_deactivation(): void {
$license=$this->license($this->customer());$key=Crypt::decryptString($license->key_encrypted);
$token=$this->postJson('/api/v1/licenses/activate',['license_key'=>$key,'device_id'=>str_repeat('a',20)])->assertOk()->json('activation_token');
$this->postJson('/api/v1/licenses/activate',['license_key'=>$key,'device_id'=>str_repeat('b',20)])->assertOk();
$this->postJson('/api/v1/licenses/activate',['license_key'=>$key,'device_id'=>str_repeat('c',20)])->assertConflict();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertOk()->assertJson(['valid'=>true]);
$this->withToken($token)->postJson('/api/v1/licenses/deactivate')->assertOk();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertForbidden();
}
public function test_refund_revokes_license_and_prevents_regrant(): void {
$user=$this->customer();$license=$this->license($user);$key=Crypt::decryptString($license->key_encrypted);$token=app(LicenseService::class)->activate($key,str_repeat('a',20))['activation_token'];
$refund=['event_id'=>'evt_refund','event_type'=>'adjustment.updated','occurred_at'=>now()->addSecond()->toISOString(),'data'=>['status'=>'approved','action'=>'refund','transaction_id'=>'txn_1']];$this->deliver($refund)->assertOk();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertForbidden();$this->deliver($this->event($user,'evt_late'))->assertOk();$this->assertDatabaseHas('licenses',['status'=>'revoked']);
}
public function test_checkout_uses_server_selected_product_and_customer(): void {$user=$this->customer();config(['tarnax.paddle.api_key'=>'secret','tarnax.paddle.client_token'=>'token']);Http::fake(['*'=>Http::response(['data'=>['id'=>'txn_checkout']])]);$this->actingAs($user)->postJson('/checkout',['price_id'=>'attacker','custom_data'=>['account_token'=>'other']])->assertOk()->assertJson(['transaction_id'=>'txn_checkout']);Http::assertSent(fn($r)=>$r['items'][0]['price_id']==='pri_test' && $r['custom_data']['account_token']===$user->account_token);}
public function test_unconfigured_checkout_fails_closed(): void {config(['tarnax.paddle.api_key'=>null]);$this->actingAs($this->customer())->postJson('/checkout')->assertStatus(503);}
public function test_downloads_require_license_signature_and_matching_customer(): void {
Storage::fake('local');Storage::disk('local')->put('releases/app.zip','binary');$release=DB::table('releases')->insertGetId(['version'=>'1.0.0','channel'=>'stable','platform'=>'macOS','notes'=>'Release','path'=>'releases/app.zip','sha256'=>hash('sha256','binary'),'bytes'=>6,'published'=>true,'created_at'=>now(),'updated_at'=>now()]);
$user=$this->customer();$other=$this->customer();$this->actingAs($user)->get('/downloads/'.$release)->assertForbidden();$this->license($user);
$this->get('/downloads/'.$release)->assertRedirect();$url=URL::temporarySignedRoute('download.file',now()->addMinutes(5),['release'=>$release,'user'=>$user->id]);$this->get($url)->assertOk();$this->actingAs($other)->get($url)->assertForbidden();
$expired=URL::temporarySignedRoute('download.file',now()->subMinute(),['release'=>$release,'user'=>$user->id]);$this->actingAs($user)->get($expired)->assertForbidden();
}
public function test_support_is_scoped_and_does_not_accept_another_ticket(): void {$owner=$this->customer();$other=$this->customer();$ticket=DB::table('tickets')->insertGetId(['user_id'=>$owner->id,'subject'=>'Private conversation','status'=>'open','created_at'=>now(),'updated_at'=>now()]);Livewire::actingAs($other)->test(SupportDesk::class)->assertDontSee('Private conversation')->call('select',$ticket)->assertForbidden();}
public function test_customer_can_create_support_ticket(): void {Livewire::actingAs($this->customer())->test(SupportDesk::class)->set('subject','Connection help')->set('body','Please help me connect my server.')->call('create')->assertHasNoErrors();$this->assertDatabaseCount('tickets',1);$this->assertDatabaseCount('ticket_messages',1);}
public function test_admin_requires_role_and_confirmed_two_factor(): void {$this->actingAs($this->customer())->get('/admin')->assertForbidden();$admin=$this->customer(['is_admin'=>true]);$this->actingAs($admin)->get('/admin')->assertRedirect('/account/security');Livewire::actingAs($admin)->test(Cms::class)->assertForbidden();}
public function test_admin_can_edit_cms_and_mutations_are_audited(): void {$admin=$this->customer(['is_admin'=>true,'two_factor_confirmed_at'=>now()]);Livewire::actingAs($admin)->test(Cms::class)->set('form',['slug'=>'new-page','title'=>'New page','body'=>'Safe content','published'=>true])->call('save')->assertHasNoErrors();$this->assertDatabaseHas('pages',['slug'=>'new-page']);$this->assertDatabaseCount('audit_logs',1);}
public function test_consent_records_explicit_choices(): void {$this->postJson('/consent',['analytics'=>false,'marketing'=>false])->assertOk();$this->assertDatabaseHas('consents',['analytics'=>false,'marketing'=>false]);}
public function test_apple_verification_fails_closed_without_root(): void {config(['tarnax.apple.root_certificate'=>null]);$this->actingAs($this->customer())->postJson('/account/apple',['signed_transaction'=>'untrusted.payload.signature'])->assertStatus(503);$this->assertDatabaseCount('purchases',0);}
public function test_account_support_and_security_render_for_verified_customer(): void {$this->actingAs($this->customer());foreach(['/account','/support','/account/security'] as $url){$this->get($url)->assertOk();}}
public function test_erasure_revokes_access_and_anonymises_profile(): void {$user=$this->customer();$this->license($user);$id=DB::table('privacy_requests')->insertGetId(['user_id'=>$user->id,'type'=>'deletion','status'=>'pending','created_at'=>now(),'updated_at'=>now()]);$admin=$this->customer(['is_admin'=>true,'two_factor_confirmed_at'=>now()]);Livewire::actingAs($admin)->test(Cms::class)->set('section','privacy_requests')->call('fulfillDeletion',$id)->assertHasNoErrors();$this->assertDatabaseHas('users',['id'=>$user->id,'name'=>'Deleted customer']);$this->assertDatabaseHas('licenses',['user_id'=>$user->id,'status'=>'revoked']);$this->assertDatabaseHas('privacy_requests',['id'=>$id,'status'=>'fulfilled']);}
use Tests\TestCase;
class CommerceTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->seed();
Notification::fake();
config(['tarnax.paddle.webhook_secret' => 'test-secret', 'tarnax.paddle.price_id' => 'pri_test']);
}
private function customer(array $attributes = []): User
{
return User::factory()->create(['account_token' => (string) Str::uuid(), ...$attributes]);
}
private function event(User $user, string $id = 'evt_1'): array
{
return ['event_id' => $id, 'event_type' => 'transaction.completed', 'occurred_at' => now()->toISOString(), 'data' => ['id' => 'txn_1', 'status' => 'completed', 'custom_data' => ['account_token' => $user->account_token], 'items' => [['price' => ['id' => 'pri_test'], 'quantity' => 1]], 'details' => ['totals' => ['grand_total' => '4900']], 'currency_code' => 'USD']];
}
private function deliver(array $event, ?int $timestamp = null, ?string $signature = null)
{
$body = json_encode($event);
$ts = $timestamp ?? time();
$sig = $signature ?? hash_hmac('sha256', $ts.':'.$body, 'test-secret');
return $this->call('POST', '/api/webhooks/paddle', [], [], [], ['CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json', 'HTTP_PADDLE_SIGNATURE' => 'ts='.$ts.';h1='.$sig], $body);
}
private function license(User $user): object
{
$this->deliver($this->event($user))->assertOk();
return DB::table('licenses')->first();
}
public function test_public_pages_and_authentication_views_render(): void
{
foreach (['/', '/releases', '/pages/privacy', '/pages/terms', '/pages/features', '/login', '/register', '/forgot-password'] as $url) {
$this->get($url)->assertOk();
}$this->get('/account')->assertRedirect('/login');
}
public function test_registration_cannot_grant_administration(): void
{
$this->post('/register', ['name' => 'Customer', 'email' => 'new@example.com', 'password' => 'StrongPass123!', 'password_confirmation' => 'StrongPass123!', 'is_admin' => true])->assertRedirect();
$this->assertFalse(User::where('email', 'new@example.com')->first()->is_admin);
}
public function test_verified_payment_issues_only_one_license_on_retries(): void
{
$user = $this->customer();
$event = $this->event($user);
$this->deliver($event)->assertOk();
$this->deliver($event)->assertOk();
$event['event_id'] = 'evt_2';
$this->deliver($event)->assertOk();
$this->assertDatabaseCount('purchases', 1);
$this->assertDatabaseCount('licenses', 1);
}
public function test_invalid_signature_and_stale_replay_never_grant_licenses(): void
{
$user = $this->customer();
$this->deliver($this->event($user), null, 'invalid')->assertUnauthorized();
$this->deliver($this->event($user), time() - 600)->assertUnauthorized();
$this->assertDatabaseCount('licenses', 0);
}
public function test_wrong_product_does_not_grant_license(): void
{
$event = $this->event($this->customer());
$event['data']['items'][0]['price']['id'] = 'pri_other';
$this->deliver($event)->assertOk();
$this->assertDatabaseCount('licenses', 0);
}
public function test_unknown_customer_rolls_back_event_for_retry(): void
{
$event = $this->event($this->customer());
$event['data']['custom_data']['account_token'] = (string) Str::uuid();
$this->deliver($event)->assertUnprocessable();
$this->assertDatabaseCount('webhook_events', 0);
}
public function test_activation_limits_validation_and_deactivation(): void
{
$license = $this->license($this->customer());
$key = Crypt::decryptString($license->key_encrypted);
$token = $this->postJson('/api/v1/licenses/activate', ['license_key' => $key, 'device_id' => str_repeat('a', 20)])->assertOk()->json('activation_token');
$this->postJson('/api/v1/licenses/activate', ['license_key' => $key, 'device_id' => str_repeat('b', 20)])->assertOk();
$this->postJson('/api/v1/licenses/activate', ['license_key' => $key, 'device_id' => str_repeat('c', 20)])->assertConflict();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertOk()->assertJson(['valid' => true]);
$this->withToken($token)->postJson('/api/v1/licenses/deactivate')->assertOk();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertForbidden();
}
public function test_refund_revokes_license_and_prevents_regrant(): void
{
$user = $this->customer();
$license = $this->license($user);
$key = Crypt::decryptString($license->key_encrypted);
$token = app(LicenseService::class)->activate($key, str_repeat('a', 20))['activation_token'];
$refund = ['event_id' => 'evt_refund', 'event_type' => 'adjustment.updated', 'occurred_at' => now()->addSecond()->toISOString(), 'data' => ['status' => 'approved', 'action' => 'refund', 'transaction_id' => 'txn_1']];
$this->deliver($refund)->assertOk();
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertForbidden();
$this->deliver($this->event($user, 'evt_late'))->assertOk();
$this->assertDatabaseHas('licenses', ['status' => 'revoked']);
}
public function test_checkout_uses_server_selected_product_and_customer(): void
{
$user = $this->customer();
config(['tarnax.paddle.api_key' => 'secret', 'tarnax.paddle.client_token' => 'token']);
Http::fake(['*' => Http::response(['data' => ['id' => 'txn_checkout']])]);
$this->actingAs($user)->postJson('/checkout', ['price_id' => 'attacker', 'custom_data' => ['account_token' => 'other']])->assertOk()->assertJson(['transaction_id' => 'txn_checkout']);
Http::assertSent(fn ($r) => $r['items'][0]['price_id'] === 'pri_test' && $r['custom_data']['account_token'] === $user->account_token);
}
public function test_unconfigured_checkout_fails_closed(): void
{
config(['tarnax.paddle.api_key' => null]);
$this->actingAs($this->customer())->postJson('/checkout')->assertStatus(503);
}
public function test_downloads_require_license_signature_and_matching_customer(): void
{
Storage::fake('local');
Storage::disk('local')->put('releases/app.zip', 'binary');
$release = DB::table('releases')->insertGetId(['version' => '1.0.0', 'channel' => 'stable', 'platform' => 'macOS', 'notes' => 'Release', 'path' => 'releases/app.zip', 'sha256' => hash('sha256', 'binary'), 'bytes' => 6, 'published' => true, 'created_at' => now(), 'updated_at' => now()]);
$user = $this->customer();
$other = $this->customer();
$this->actingAs($user)->get('/downloads/'.$release)->assertForbidden();
$this->license($user);
$this->get('/downloads/'.$release)->assertRedirect();
$url = URL::temporarySignedRoute('download.file', now()->addMinutes(5), ['release' => $release, 'user' => $user->id]);
$this->get($url)->assertOk();
$this->actingAs($other)->get($url)->assertForbidden();
$expired = URL::temporarySignedRoute('download.file', now()->subMinute(), ['release' => $release, 'user' => $user->id]);
$this->actingAs($user)->get($expired)->assertForbidden();
}
public function test_support_is_scoped_and_does_not_accept_another_ticket(): void
{
$owner = $this->customer();
$other = $this->customer();
$ticket = DB::table('tickets')->insertGetId(['user_id' => $owner->id, 'subject' => 'Private conversation', 'status' => 'open', 'created_at' => now(), 'updated_at' => now()]);
Livewire::actingAs($other)->test(SupportDesk::class)->assertDontSee('Private conversation')->call('select', $ticket)->assertForbidden();
}
public function test_customer_can_create_support_ticket(): void
{
Livewire::actingAs($this->customer())->test(SupportDesk::class)->set('subject', 'Connection help')->set('body', 'Please help me connect my server.')->call('create')->assertHasNoErrors();
$this->assertDatabaseCount('tickets', 1);
$this->assertDatabaseCount('ticket_messages', 1);
}
public function test_admin_requires_role_and_confirmed_two_factor(): void
{
$this->actingAs($this->customer())->get('/admin')->assertForbidden();
$admin = $this->customer(['is_admin' => true]);
$this->actingAs($admin)->get('/admin')->assertRedirect('/account/security');
Livewire::actingAs($admin)->test(Cms::class)->assertForbidden();
}
public function test_admin_can_edit_cms_and_mutations_are_audited(): void
{
$admin = $this->customer(['is_admin' => true, 'two_factor_confirmed_at' => now()]);
Livewire::actingAs($admin)->test(Cms::class)->set('form', ['slug' => 'new-page', 'title' => 'New page', 'body' => 'Safe content', 'published' => true])->call('save')->assertHasNoErrors();
$this->assertDatabaseHas('pages', ['slug' => 'new-page']);
$this->assertDatabaseCount('audit_logs', 1);
}
public function test_consent_records_explicit_choices(): void
{
$this->postJson('/consent', ['analytics' => false, 'marketing' => false])->assertOk();
$this->assertDatabaseHas('consents', ['analytics' => false, 'marketing' => false]);
}
public function test_apple_verification_fails_closed_without_root(): void
{
config(['tarnax.apple.root_certificate' => null]);
$this->actingAs($this->customer())->postJson('/account/apple', ['signed_transaction' => 'untrusted.payload.signature'])->assertStatus(503);
$this->assertDatabaseCount('purchases', 0);
}
public function test_account_support_and_security_render_for_verified_customer(): void
{
$this->actingAs($this->customer())->withSession(['auth.password_confirmed_at' => time()]);
foreach (['/account', '/support', '/account/security'] as $url) {
$this->get($url)->assertOk();
}
}
public function test_erasure_revokes_access_and_anonymises_profile(): void
{
$user = $this->customer();
$this->license($user);
$id = DB::table('privacy_requests')->insertGetId(['user_id' => $user->id, 'type' => 'deletion', 'status' => 'pending', 'created_at' => now(), 'updated_at' => now()]);
$admin = $this->customer(['is_admin' => true, 'two_factor_confirmed_at' => now()]);
Livewire::actingAs($admin)->test(Cms::class)->set('section', 'privacy_requests')->call('fulfillDeletion', $id)->assertHasNoErrors();
$this->assertDatabaseHas('users', ['id' => $user->id, 'name' => 'Deleted customer']);
$this->assertDatabaseHas('licenses', ['user_id' => $user->id, 'status' => 'revoked']);
$this->assertDatabaseHas('privacy_requests', ['id' => $id, 'status' => 'fulfilled']);
}
public function test_refund_before_purchase_prevents_late_grant(): void
{
$user = $this->customer();
$this->deliver(['event_id' => 'early_refund', 'event_type' => 'adjustment.created', 'occurred_at' => now()->toISOString(), 'data' => ['status' => 'approved', 'action' => 'refund', 'transaction_id' => 'txn_1']])->assertOk();
$this->deliver($this->event($user))->assertOk();
$this->assertDatabaseCount('licenses', 0);
}
public function test_subscription_events_revoke_and_restore_and_ignore_stale_events(): void
{
$user = $this->customer();
$event = $this->event($user);
$event['data']['subscription_id'] = 'sub_1';
$this->deliver($event)->assertOk();
$send = function ($id, $status, $seconds) {
return $this->deliver(['event_id' => $id, 'event_type' => 'subscription.updated', 'occurred_at' => now()->addSeconds($seconds)->toISOString(), 'data' => ['id' => 'sub_1', 'status' => $status]]);
};
$send('pause', 'paused', 10)->assertOk();
$this->assertDatabaseHas('licenses', ['status' => 'revoked']);
$send('resume', 'active', 20)->assertOk();
$this->assertDatabaseHas('licenses', ['status' => 'active']);
$send('old_pause', 'paused', 5)->assertOk();
$this->assertDatabaseHas('licenses', ['status' => 'active']);
}
public function test_admin_can_manage_navigation_and_reject_external_redirects(): void
{
$admin = $this->customer(['is_admin' => true, 'two_factor_confirmed_at' => now()]);
Livewire::actingAs($admin)->test(Cms::class)->set('section', 'navigation')->set('form', ['label' => 'Features', 'path' => '/pages/features', 'position' => 1, 'published' => true])->call('saveResource')->assertHasNoErrors();
$this->assertDatabaseHas('navigation', ['label' => 'Features']);
Livewire::actingAs($admin)->test(Cms::class)->set('section', 'redirects')->set('form', ['source' => '/old', 'destination' => '//evil.example'])->call('saveResource')->assertHasErrors('form.destination');
}
public function test_published_page_html_is_escaped(): void
{
DB::table('pages')->where('slug', 'features')->update(['body' => '<script>alert(1)</script>']);
$this->get('/pages/features')->assertSee('&lt;script&gt;', false)->assertDontSee('<script>alert(1)</script>', false);
}
public function test_apple_notification_fails_closed_without_configuration(): void
{
config(['tarnax.apple.root_certificate' => null]);
$this->postJson('/api/webhooks/apple', ['signedPayload' => 'untrusted.payload.signature'])->assertStatus(503);
}
public function test_erased_customer_cannot_use_an_old_activation_token(): void
{
$user = $this->customer();
$license = $this->license($user);
$token = app(LicenseService::class)->activate(Crypt::decryptString($license->key_encrypted), str_repeat('x', 20))['activation_token'];
$id = DB::table('privacy_requests')->insertGetId(['user_id' => $user->id, 'type' => 'deletion', 'status' => 'pending', 'created_at' => now(), 'updated_at' => now()]);
$admin = $this->customer(['is_admin' => true, 'two_factor_confirmed_at' => now()]);
Livewire::actingAs($admin)->test(Cms::class)->set('section', 'privacy_requests')->call('fulfillDeletion', $id);
$this->withToken($token)->postJson('/api/v1/licenses/validate')->assertForbidden();
}
public function test_unverified_accounts_cannot_checkout_or_open_portal(): void
{
$user = $this->customer(['email_verified_at' => null]);
$this->actingAs($user)->get('/account')->assertRedirect('/email/verify');
$this->postJson('/checkout')->assertForbidden();
}
public function test_private_media_is_safely_served(): void
{
Storage::fake('local');
Storage::disk('local')->put('media/test.png','test image');
$id = DB::table('media')->insertGetId(['title' => 'App', 'alt' => 'Icon', 'path' => 'media/test.png', 'mime' => 'image/png', 'bytes' => 10, 'created_at' => now(), 'updated_at' => now()]);
$this->get('/media/'.$id)->assertOk()->assertHeader('X-Content-Type-Options','nosniff');
}
}
@@ -3,11 +3,13 @@
namespace Tests\Feature;
// use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use \Illuminate\Foundation\Testing\RefreshDatabase;
use RefreshDatabase;
/**
* A basic test example.
*/