Developer-Dashboard
view release on metacpan or search on metacpan
integration/blank-env/run-integration.pl view on Meta::CPAN
my $action = _run_shell( 'dashboard action run system-status paths', $project_cd . 'dashboard action run system-status paths' );
_assert_match( $action->{stdout}, qr/runtime/, 'dashboard action run executes builtin action' );
my $collector_write = _run_shell( 'dashboard collector write-result', $project_cd . q{printf 'manual-output' | dashboard collector write-result manual.collector 0} );
_assert( $collector_write->{exit_code} == 0, 'dashboard collector write-result accepts manual output' );
my $fake_collector_run = _run_shell( 'dashboard collector run fake.config.collector', $project_cd . 'dashboard collector run fake.config.collector' );
_assert_match( $fake_collector_run->{stdout}, qr/"exit_code"\s*:\s*0/, 'dashboard collector run succeeds for fake project config collector' );
my $collector_list = _run_shell( 'dashboard collector list', $project_cd . 'dashboard collector list' );
_assert_match( $collector_list->{stdout}, qr/manual\.collector/, 'dashboard collector list shows stored collectors' );
_assert_match( $collector_list->{stdout}, qr/fake\.config\.collector/, 'dashboard collector list shows fake project config collector' );
my $collector_job = _run_shell( 'dashboard collector job', $project_cd . 'dashboard collector job fake.config.collector' );
_assert_match( $collector_job->{stdout}, qr/"command"/, 'dashboard collector job returns job metadata' );
my $collector_status = _run_shell( 'dashboard collector status', $project_cd . 'dashboard collector status fake.config.collector' );
_assert_match( $collector_status->{stdout}, qr/"enabled"/, 'dashboard collector status returns status data' );
my $collector_output = _run_shell( 'dashboard collector output', $project_cd . 'dashboard collector output fake.config.collector' );
_assert_match( $collector_output->{stdout}, qr/fake config collector output/, 'dashboard collector output returns prepared output' );
my $collector_inspect = _run_shell( 'dashboard collector inspect', $project_cd . 'dashboard collector inspect fake.config.collector' );
_assert_match( $collector_inspect->{stdout}, qr/"job"|"status"|"output"/, 'dashboard collector inspect returns combined view' );
my $collector_start = _run_shell( 'dashboard collector start', $project_cd . 'dashboard collector start fake.config.collector' );
_assert_match( $collector_start->{stdout}, qr/\d+/, 'dashboard collector start returns a pid' );
sleep 2;
my $collector_restart = _run_shell( 'dashboard collector restart', $project_cd . 'dashboard collector restart fake.config.collector' );
_assert_match( $collector_restart->{stdout}, qr/\d+/, 'dashboard collector restart returns a pid' );
my $collector_stop = _run_shell( 'dashboard collector stop', $project_cd . 'dashboard collector stop fake.config.collector' );
_assert_match( $collector_stop->{stdout}, qr/\d+/, 'dashboard collector stop returns the stopped pid' );
my $collector_log = _run_shell( 'dashboard collector log', $project_cd . 'dashboard collector log' );
_assert( defined $collector_log->{stdout}, 'dashboard collector log returns log text' );
my $auth_add = _run_shell( 'dashboard auth add-user', $project_cd . q{dashboard auth add-user explicit_helper explicit-pass-123} );
_assert_match( $auth_add->{stdout}, qr/"username"\s*:\s*"explicit_helper"/, 'dashboard auth add-user creates helper user' );
my $auth_list = _run_shell( 'dashboard auth list-users', $project_cd . 'dashboard auth list-users' );
_assert_match( $auth_list->{stdout}, qr/"explicit_helper"/, 'dashboard auth list-users includes explicit helper' );
my $auth_remove = _run_shell( 'dashboard auth remove-user', $project_cd . 'dashboard auth remove-user explicit_helper' );
_assert_match( $auth_remove->{stdout}, qr/"removed"\s*:\s*"explicit_helper"/, 'dashboard auth remove-user removes explicit helper' );
my $docker_dry = _run_shell(
'dashboard docker compose --dry-run',
'dashboard docker compose --project ' . _shell_quote($compose) . ' --dry-run config'
);
_assert_match( $docker_dry->{stdout}, qr/"command"\s*:/, 'dashboard docker compose dry-run returns resolved command' );
_assert_match( $docker_dry->{stdout}, qr/compose\.yaml/, 'dashboard docker compose dry-run includes compose file' );
my $serve = _run_shell( 'dashboard serve', $project_cd . 'dashboard serve' );
_assert_match( $serve->{stdout}, qr/"pid"\s*:/, 'dashboard serve starts background web service' );
_wait_for_http( 'http://127.0.0.1:7890/', 200 );
my $blocked_transient = _run_shell(
'curl transient token denied by default',
'curl -sS -o /tmp/transient-denied.body -w \'%{http_code}\' ' . _shell_quote( 'http://127.0.0.1:7890/?token=' . $token ),
);
_assert_match( $blocked_transient->{stdout}, qr/^403$/, 'loopback transient token route is denied by default' );
_assert_match( _read_text('/tmp/transient-denied.body'), qr/Transient token URLs are disabled/, 'loopback transient token denial explains the policy' );
my $root = _run_shell( 'curl loopback root', q{curl -fsS http://127.0.0.1:7890/} );
_assert_match( $root->{stdout}, qr/instruction-editor/, 'loopback root serves the bookmark editor' );
my $root_dom = _run_browser_dom( 'browser loopback root', 'http://127.0.0.1:7890/', user_data_dir => $profile );
_assert_match( $root_dom, qr/instruction-editor/, 'browser loopback root renders the editor DOM' );
_assert_match( $root_dom, qr/TITLE:\s+Developer Dashboard/, 'browser loopback root shows bookmark source text' );
my $project_dom = _run_browser_dom( 'browser fake project page', 'http://127.0.0.1:7890/app/project-home', user_data_dir => $profile );
_assert_match( $project_dom, qr/project-marker/, 'browser renders fake project bookmark page' );
_assert_match( $project_dom, qr/Fake Project Home/, 'browser renders fake project bookmark content' );
_assert_match( $project_dom, qr/dashboard-nav-items/, 'browser renders shared nav container on fake project bookmark page' );
_assert_match( $project_dom, qr/Alpha Nav Current/, 'browser renders shared nav TT output against the outer page path' );
_assert_match( $project_dom, qr{<div id="nav-beta">/app/project-home / /app/project-home</div>}, 'browser exposes current_page through env and env.runtime_context for shared nav TT fragments' );
_assert( index( $project_dom, 'nav-alpha' ) < index( $project_dom, 'nav-beta' ), 'browser renders shared nav bookmark fragments in sorted filename order' );
_assert( index( $project_dom, 'dashboard-nav-items' ) < index( $project_dom, 'project-marker' ), 'browser renders shared nav fragments before the main page body' );
my $legacy_ajax_page = _run_shell( 'curl legacy ajax saved page', q{curl -fsS http://127.0.0.1:7890/app/legacy-ajax} );
_assert_match( $legacy_ajax_page->{stdout}, qr{/ajax/project-endpoint\.json\?type=text}, 'saved bookmark Ajax renders a stable file-backed ajax endpoint by default' );
my $legacy_ajax_saved = _run_shell( 'curl saved bookmark ajax endpoint', q{curl -fsS 'http://127.0.0.1:7890/ajax/project-endpoint.json?type=text'} );
_assert_match( $legacy_ajax_saved->{stdout}, qr/saved-start/, 'saved bookmark ajax endpoint streams direct perl stdout' );
_assert_match( $legacy_ajax_saved->{stdout}, qr/saved-warn/, 'saved bookmark ajax endpoint streams perl stderr warnings' );
_assert_match( $legacy_ajax_saved->{stdout}, qr/saved-child-out/, 'saved bookmark ajax endpoint streams child stdout' );
_assert_match( $legacy_ajax_saved->{stdout}, qr/saved-child-err/, 'saved bookmark ajax endpoint streams child stderr' );
_assert_match( $legacy_ajax_saved->{stdout}, qr/saved-die/, 'saved bookmark ajax endpoint streams uncaught perl die output' );
my $legacy_ajax_stream_page = _run_shell( 'curl legacy ajax stream saved page', q{curl -fsS http://127.0.0.1:7890/app/legacy-ajax-stream} );
_assert_match( $legacy_ajax_stream_page->{stdout}, qr{/ajax/project-stream\.txt\?type=text}, 'saved bookmark ajax stream page renders a stable default text ajax endpoint' );
my $legacy_ajax_stream = _capture_stream_prefix(
'curl saved bookmark ajax stream endpoint',
q{curl --no-buffer -fsS 'http://127.0.0.1:7890/ajax/project-stream.txt'},
expected_chunks => [ 'stream1', 'stream2' ],
timeout => 4,
);
_assert( @{ $legacy_ajax_stream->{events} || [] } >= 2, 'saved bookmark ajax stream endpoint produced multiple early chunks before process exit' );
_assert( ( $legacy_ajax_stream->{events}[0]{at} || 99 ) < 1.5, 'saved bookmark ajax stream endpoint flushes the first chunk before the long-running ajax loop finishes' );
_assert( ( $legacy_ajax_stream->{events}[1]{at} || 99 ) < 2.5, 'saved bookmark ajax stream endpoint keeps flushing later chunks during the long-running ajax loop' );
my $container_ip = _trim( _run_shell( 'container ip', q{hostname -I | awk '{print $1}'} )->{stdout} );
_assert( $container_ip ne '', 'container ip discovered for helper-access path' );
my $helper_root_disabled = _run_shell(
'curl helper root before helper user exists',
'curl -sS -o /tmp/helper-root.html -w \'%{http_code}\' http://' . $container_ip . ':7890/'
);
_assert_match( $helper_root_disabled->{stdout}, qr/^401$/, 'non-loopback self-access stays unauthorized before any helper user exists' );
_assert( _read_text('/tmp/helper-root.html') eq q{}, 'outsider bootstrap response keeps the body empty before any helper user exists' );
_assert( _read_text('/tmp/helper-root.html') !~ /<form[^>]*action="\/login"/, 'outsider bootstrap response does not expose the login form before any helper user exists' );
my $helper_disabled_dom = _run_browser_dom( 'browser helper root before helper user exists', "http://$container_ip:7890/", user_data_dir => $profile );
_assert_match( $helper_disabled_dom, qr/HTTP ERROR 401/, 'browser outsider bootstrap response resolves to a generic 401 browser error page before any helper user exists' );
_assert( $helper_disabled_dom !~ /Helper access is disabled until a helper user is added\./, 'browser outsider bootstrap response does not leak helper bootstrap guidance before any helper user exists' );
_assert( $helper_disabled_dom !~ /action="\/login"/, 'browser outsider bootstrap response omits the login form before any helper user exists' );
_run_shell( 'dashboard auth add helper-login user', $project_cd . q{dashboard auth add-user helper_login helper-login-pass-123} );
my $helper_root = _run_shell(
'curl helper root after helper user exists',
'curl -sS -o /tmp/helper-root-after-enable.html -w \'%{http_code}\' http://' . $container_ip . ':7890/'
);
_assert_match( $helper_root->{stdout}, qr/^401$/, 'non-loopback self-access returns helper login after a helper user exists' );
_assert_match( _read_text('/tmp/helper-root-after-enable.html'), qr/<form[^>]*action="\/login"/, 'helper root serves login page after a helper user exists' );
my $helper_dom = _run_browser_dom( 'browser helper root after helper user exists', "http://$container_ip:7890/", user_data_dir => $profile );
_assert_match( $helper_dom, qr/action="\/login"/, 'browser helper root renders login form after a helper user exists' );
( run in 1.250 second using v1.01-cache-2.11-cpan-39bf76dae61 )