Database-BI

 view release on metacpan or  search on metacpan

t/transaction.t  view on Meta::CPAN


		# Phase 4: filter on a TSV column.
		$t->get_ok('/view/employees?f=' . url_escape('Department:eq:Engineering'))
		  ->status_is(200, 'Phase 4: filtered /view returns 200');
		$t->content_like(qr/Alice/, 'Phase 4: filter keeps matching row');
		$t->content_unlike(qr/Marketing/, 'Phase 4: filter removes non-matching row');

		# Phase 5: export to CSV.
		$t->get_ok('/export?l=' . url_escape('table:employees') . '&format=csv')
		  ->status_is(200, 'Phase 5: CSV export returns 200')
		  ->content_type_like(qr{text/csv}, 'Phase 5: content-type is text/csv')
		  ->content_like(qr/Alice/, 'Phase 5: exported CSV contains data');

		# Phase 6: export to SQLite (requires DBD::SQLite).
		SKIP: {
			eval { DBI->install_driver('SQLite') }
				or skip 'DBD::SQLite not available for SQLite export', 2;
			$t->get_ok('/export?l=' . url_escape('table:employees') . '&format=sqlite')
			  ->status_is(200, 'Phase 6: SQLite export returns 200')
			  ->content_type_like(qr{sqlite}, 'Phase 6: content-type contains sqlite');
		}
	}

	# Phase 7: upload a TSV file and open it (always run if upload route works).
	SKIP: {
		my $tsv_body = "item\tqty\tprice\nWidget\t10\t4.99\nGadget\t5\t9.99\n";
		$t->post_ok('/upload',
			form => { file => { content => $tsv_body, filename => 'tmpparts.tsv' } }
		)->status_is(200, 'Phase 7: TSV upload returns 200');

t/transaction.t  view on Meta::CPAN


		# Phase 4: filter works on .sqlite3 source.
		$t->get_ok('/open?path=' . url_escape($db_path) . '&f=' . url_escape('name:eq:Sprocket'))
		  ->status_is(200, 'Phase 4: filtered /open returns 200');
		$t->content_like(qr/Sprocket/, 'Phase 4: filter keeps matching row');
		$t->content_unlike(qr/Flywheel/, 'Phase 4: filter removes non-matching row');

		# Phase 5: CSV export of a .sqlite3 source.
		$t->get_ok('/export?l=' . url_escape("path:$db_path") . '&format=csv')
		  ->status_is(200, 'Phase 5: CSV export of .sqlite3 returns 200')
		  ->content_type_like(qr{text/csv}, 'Phase 5: content-type is text/csv');

		# Phase 6: browse directory shows the .sqlite3 file.
		$t->get_ok('/browse?path=' . url_escape($dir))
		  ->status_is(200, 'Phase 6: browse dir returns 200');
		$t->content_like(qr/widgets\.sqlite3/, 'Phase 6: .sqlite3 file listed in browser');

		# Phase 7: idempotency -- second request hits the cache.
		$t->get_ok('/open?path=' . url_escape($db_path))
		  ->status_is(200, 'Phase 7: second /open is idempotent');

t/transaction.t  view on Meta::CPAN


		# Phase 4: filter works on .sqlite source.
		$t->get_ok('/open?path=' . url_escape($db_path) . '&f=' . url_escape('sensor:eq:Alpha'))
		  ->status_is(200, 'Phase 4: filtered /open returns 200');
		$t->content_like(qr/Alpha/, 'Phase 4: filter keeps matching row');
		$t->content_unlike(qr/Beta/, 'Phase 4: filter removes non-matching row');

		# Phase 5: CSV export of a .sqlite source.
		$t->get_ok('/export?l=' . url_escape("path:$db_path") . '&format=csv')
		  ->status_is(200, 'Phase 5: CSV export of .sqlite returns 200')
		  ->content_type_like(qr{text/csv}, 'Phase 5: content-type is text/csv');

		# Phase 6: browse directory shows the .sqlite file.
		$t->get_ok('/browse?path=' . url_escape($dir))
		  ->status_is(200, 'Phase 6: browse dir returns 200');
		$t->content_like(qr/readings\.sqlite/, 'Phase 6: .sqlite file listed in browser');

		# Phase 7: idempotency -- second request hits the cache.
		$t->get_ok('/open?path=' . url_escape($db_path))
		  ->status_is(200, 'Phase 7: second /open is idempotent');

t/unit.t  view on Meta::CPAN

	}
	delete $ledger{'DataSource.fetch_all.ok'};
};

# ---------------------------------------------------------------------------
# HTTP API tests via Test::Mojo
# ---------------------------------------------------------------------------

# ---  GET /  ----------------------------------------------------------------
subtest 'GET / -- home page lists available tables' => sub {
	# Status and content-type smoke test.
	$t->get_ok('/')->status_is(200)->content_type_like(qr{text/html})
	  # The Browse card must contain a path text input (id="bi-path-input")
	  # and its form must target /open.  These assertions exist specifically
	  # to catch the regression where the path input was silently removed
	  # from home.html.tt without any test failing.
	  ->content_like(qr/id="bi-path-input"/, 'home page has path input field')
	  ->content_like(qr/action="\/open"/, 'Browse card form targets /open')
	  # The layout writes the resolved language into <html lang>.  index() rendered
	  # its template without passing it, so the attribute was empty here while the
	  # tutorial's page carried the language - a screen reader and a translation

t/unit.t  view on Meta::CPAN

	delete $ledger{'GET./combine.404'};
};

# ---  GET /export?format=json  ----------------------------------------------

subtest 'GET /export?l=table:sales&format=json -- 200 application/json' => sub {
	# The JSON export format is documented in the POD alongside csv and sqlite.
	# Verify it produces a JSON content type and a non-empty body.
	$t->get_ok("/export?l=table:$SALES_TABLE&format=json")
	  ->status_is(200, 'JSON export returns 200')
	  ->content_type_like(qr{application/json}, 'content-type is JSON');
	delete $ledger{'GET./export.json'};
};

# ---  GET /graph error paths  -----------------------------------------------

subtest 'GET /graph (no x= or y=) -- 400 Missing x or y column parameter' => sub {
	# graph_view checks for both x= and y= before opening the data source.
	# Missing either produces an immediate 400 with a plain-text error.
	$t->get_ok("/graph?l=table:$SALES_TABLE&x=product")
	  ->status_is(400, '400 when y= is absent')



( run in 1.648 second using v1.01-cache-2.11-cpan-99fa8243a42 )