E2E connection stores and database lifecycle
For the operational tiger-sqlcmd guide—including bootstrap setup, disposable database
and read-only clone examples, parallel-agent isolation, cleanup, and recovery—see
TigerSqlCmd E2E scenarios. This document remains the
architecture and safety-contract reference.
The CLI examples here use --non-interactive because repository E2E tests, CI jobs,
scripts, and agents must fail on missing input instead of opening a menu or prompt. The
flag selects TigerCli's unattended interaction policy for the same commands; it does not
relax the lifecycle contract described on this page. See
One Command Model, Multiple Interaction Modes
for the operational explanation.
TigerQuery's end-to-end infrastructure runs SQL-backed tests only against a connection that a user or operator deliberately selected and authorized. It combines four durable contracts:
- one explicitly selected connection-store file;
- an expected bootstrap profile name plus exact reserved authorization metadata;
- resolution that reads configuration but never probes SQL Server;
- a database lifecycle that creates and cleans up only the exact database owned by that lifecycle instance.
The central safety rule is:
Reachability is not authorization.
A server that answers, an ordinary saved profile, a familiar local instance name, or a database with a recognizable prefix does not grant permission to run E2E work or delete anything.
Goals and safety model
The infrastructure is designed to make a fresh checkout inert while keeping deliberate local, CI, and container setup straightforward. It must:
- use the same named connection profiles as TigerQuery-family applications;
- let a host define its normal store and expected bootstrap name;
- support an isolated store override without turning the override into an enable switch;
- distinguish missing configuration from malformed or unauthorized configuration;
- keep secrets out of command lines, persisted resolved values, and diagnostics;
- create unique databases only with an explicit database-creation grant;
- establish ownership from one successful create operation, not from naming patterns;
- report possible orphans without deleting them;
- runtime-skip unconfigured xUnit tests before any SQL activity.
Safety failures are intentionally noisy. A missing host-default profile is normal
NotConfigured state; an unreadable store, malformed metadata, duplicate selected name,
missing permission, or invalid profile is a failure. Quiet fallback would turn a typo or
corrupt configuration into execution against a different store or server.
No discovery or probing
SqlServerE2eConnectionResolver reads one selected SqlServerConnectionStore, selects by
name, validates reserved metadata and the profile, and returns. It does not:
- try
.,(local),localhost, LocalDB, named instances, ports, services, or containers; - enumerate SQL Server instances or inspect running processes;
- open a
SqlConnection, test credentials, check reachability, or inspect permissions; - scan profiles and choose the first one that connects;
- create a store file, profile, database, or directory.
The repository live-test harness likewise reads only
TIGERQUERY_CONNECTION_STORE_FILE for store selection. Legacy endpoint environment
variables and reachable local servers cannot activate the suite.
Selecting one connection store
TigerQuery standardizes this precedence:
- an explicit path supplied by the caller, such as
--tq-connection-store-file <path>; TIGERQUERY_CONNECTION_STORE_FILE;- the host application's
DefaultConnectionStoreFile.
SqlServerConnectionStorePathResolver normalizes the winning value to an absolute path
and reports its SqlServerConnectionStorePathSource. A source that supplies no value is
skipped. A source that supplies a blank, malformed, or directory-only path decides the
outcome and fails; TigerQuery never falls through to a lower-priority store.
Path resolution is syntactic and inert. It does not check whether the file exists, touch
the filesystem, or open SQL. An absent selected store later loads as empty for E2E
resolution and normally yields NotConfigured. An existing store that cannot be read or
parsed yields Invalid; it is never mistaken for an empty store.
Every operation on a constructed SqlServerConnectionStore uses that instance's one
normalized FilePath. There is no hidden second lookup. Mutations use the store's normal
coordinated, atomic persistence path; a failed mutation leaves the previous store intact.
tiger-sqlcmd store choices
tiger-sqlcmd uses the shared per-user store returned by:
SqlServerConnectionStoreOptions.Shared("ItTiger.net").FilePath
An environment or command-line override changes only the store path. The selected store must independently contain the expected bootstrap name and all required metadata; selecting a path does not authorize E2E work. The complete regular-store and isolated-store setup is in TigerSqlCmd E2E scenarios.
TigerCli integration
ItTiger.TigerQuery.CliCore exposes TigerQueryCliContribution and
TigerQueryCliOptions so hosts use the same option, environment variable, precedence,
and failure rules as tiger-sqlcmd.
using ItTiger.TigerCli.Commands;
using ItTiger.TigerQuery.CliCore;
using ItTiger.TigerQuery.Core;
var contribution = new TigerQueryCliContribution(new TigerQueryCliOptions
{
DefaultConnectionStoreFile =
SqlServerConnectionStoreOptions.AppSpecific("Example", "example-sql").FilePath,
DefaultE2eBootstrapConnectionName = "example-sql-e2e"
});
var app = TigerCliApp.CreateBuilder()
.UseAssemblyMetadata(typeof(Program).Assembly)
.UseAppResources(SqlServerConnectionCommands.CreateAppResources())
.AddContribution(contribution)
.AddCommandGroup("connection", group =>
{
SqlServerConnectionCommands.Configure(group, options =>
{
options.TigerQuery = contribution.Options;
options.ValidationPolicy =
SqlServerConnectionValidationPolicy.DatabaseOptional;
});
})
.Build();
Create one TigerQueryCliOptions instance and share it with the contribution, connection
commands, providers, and host services. The contribution runs once before settings are
bound, resolves the store path, and stores the result. TigerQueryCliOptions.Store is
then created lazily and reused for the run. A malformed override fails before a command
handler, even for a command that would not otherwise open the store.
Registering the contribution and mounting the connection group are separate opt-ins.
Register the contribution at most once. It also contributes help metadata for
TIGERQUERY_CONNECTION_STORE_FILE; a host adopting it must not register the same
environment variable independently.
E2E metadata and bootstrap selection
TigerQuery owns the ordinal, case-sensitive ittiger.e2e.* namespace. The namespace
currently defines these keys:
ittiger.e2e.enabled=true
ittiger.e2e.bootstrap=true
ittiger.e2e.allow-database-create=true
| Key | Meaning |
|---|---|
ittiger.e2e.enabled |
The profile may be used for TigerQuery E2E work. |
ittiger.e2e.bootstrap |
The selected profile may act as an E2E bootstrap. |
ittiger.e2e.allow-database-create |
E2E infrastructure may create databases through the profile. |
ittiger.e2e.session-id |
Exact session safety-correlation GUID for a generated non-bootstrap connection. |
ittiger.e2e.database.name |
Exact database targeted by that connection. |
ittiger.e2e.database.allow-drop |
Exact lowercase Boolean granting or withholding database ownership. |
Only the exact lowercase values true and false are valid. True, 1, yes, an
empty value, and values with surrounding whitespace are malformed. Malformed reserved
metadata is an error, not a denial or an absent flag.
The complete lowercase ittiger.e2e.* namespace is reserved for TigerQuery. Generic profile
and CLI metadata mutation rejects keys in that namespace. Applications must use their own
prefix. Unknown reserved keys are preserved and ignored by older readers so newer stores
remain forward-compatible. Use SqlServerE2eMetadata.AuthorizeNewProfile or
AuthorizeNewBootstrapProfile, rather than generic metadata setters, to write the known
authorization keys.
Expected bootstrap name
tiger-sqlcmd configures the default bootstrap name:
tiger-sqlcmd-e2e
Core itself invents no global default; a library host supplies
SqlServerE2eConnectionResolutionOptions.DefaultConnectionName or an explicit
ConnectionName.
Name and metadata have separate mandatory roles:
- the explicit caller name, or host default name, identifies the intended profile;
- exact
enabled=trueandbootstrap=truemetadata authorizes that selected profile.
Neither is sufficient alone. A conventionally named ordinary profile is not authorized,
and a store containing one authorized bootstrap is not selected implicitly when no name
is supplied. Store order and connection reachability never break a tie. Database-creating
work additionally requests and requires exact allow-database-create=true.
SqlServerE2eConnectionResolver.Resolve returns one of four statuses:
| Status | Meaning |
|---|---|
Resolved |
Exactly one named profile is complete and has every required authorization. This is the only status carrying a profile. |
NotConfigured |
No name is available, the selected store is absent/empty, or the host-default name has not been created. Test infrastructure may skip. |
Ambiguous |
Duplicate requested names or multiple authorized candidates with no supplied name make selection unsafe. Never choose the first. |
Invalid |
An explicit name is missing, the store is unreadable, metadata is absent/malformed, a permission is missing, or profile validation fails. Fail rather than skip. |
The explicit ConnectionName and host DefaultConnectionName intentionally fail
differently. A caller explicitly naming a missing profile made an invalid request; a
host convention that has never been configured describes a clean machine.
Connection-management command boundary
connection add --allow-database-create requires --e2e. Regular add --e2e writes
ittiger.e2e.enabled=true and, when requested, the database-creation flag. It never writes
ittiger.e2e.bootstrap=true.
The dedicated connection add-e2e-bootstrap [--name <name>] command writes
enabled=true and bootstrap=true, plus
allow-database-create=true only when requested. It is add-only and refuses to overwrite
an existing profile. Ordinary edit preserves reserved metadata but generic --metadata
and --remove-metadata cannot grant, alter, or remove reserved authorization.
Older E2E profiles without bootstrap=true are invalid bootstraps. Preserve any needed
settings, then recreate them with connection add-e2e-bootstrap, or use the public
TigerQuery-owned authorization API and persist the updated profile. Regular
connection add --e2e is not a bootstrap migration.
See TigerSqlCmd E2E scenarios for verified CLI invocations and secret-reference setup.
Session-scoped CLI lifecycle
The tiger-sqlcmd e2e group is a durable, connection-record-driven lifecycle. Every
command requires a non-empty GUID --session-id; it is a safety correlation value, not a
secret.
One command creates both a new database and a saved connection targeting it. There is no
database-only create command. The authorized bootstrap must have exact
enabled=true, bootstrap=true, and allow-database-create=true. The copied profile
preserves authentication and unresolved external references; resolved values are never
persisted. If connection persistence fails, TigerQuery attempts to roll back only the exact
database created by that invocation and distinguishes successful rollback from a rollback
failure that needs manual attention.
The generated database is _TQ_E2E_<database-part>_<suffix> and the connection is
E2E-<connection-part>-<suffix>. --name-part defaults both parts;
--database-name-part and --connection-name-part override their respective part. Without
any part, session is used. Name parts are trimmed, runs of unsupported characters become
one hyphen, and the result must contain an ASCII letter or digit and fit the SQL identifier
limit. Prefixes are fixed and the same random suffix is used for both names.
The paired connection receives the complete protected schema:
ittiger.e2e.enabled=true
ittiger.e2e.bootstrap=false
ittiger.e2e.allow-database-create=false
ittiger.e2e.session-id=<canonical-guid>
ittiger.e2e.database.name=<exact-created-database-name>
ittiger.e2e.database.allow-drop=true
connection clone-e2e performs no SQL operation. It preserves authentication and unresolved external
references, retargets the clone to the exact selected database, rejects a generated name
that already exists, and writes the same protected schema with
ittiger.e2e.database.allow-drop=false. This is the supported path for pre-existing or
read-only databases.
e2e drop requires exact enabled=true, bootstrap=false, and session-ID metadata. It reads the
database name and ownership Boolean only from protected metadata. With allow-drop=true,
it additionally validates the exact _TQ_E2E_ prefix, drops through the authorized
bootstrap, and removes the connection only after the drop succeeds or the exact database
is confirmed absent. With allow-drop=false, it never connects to SQL and removes only the
saved connection.
Because a database TigerQuery owns is disposable and its teardown has to be deterministic,
the allow-drop=true path does not merely attempt DROP DATABASE. Once every check above
has passed, it submits one guarded batch on one connection that tests the exact name with a
bound parameter, sets the database SINGLE_USER WITH ROLLBACK IMMEDIATE, and drops it. Work
left open by an interrupted or abandoned session in that database is rolled back. Keeping
the mode change and the drop in one batch is what stops another connection from taking the
single-user slot in between. A database that is already absent makes the batch a no-op and
the connection record is still removed; a failure keeps the record for an exact retry.
This is the only place TigerQuery forces anything. It is unreachable from an
allow-drop=false record, which authorizes no SQL whatsoever against its target, and it is
never reached by name, prefix, age, or apparent inactivity.
Cleanup enumerates matching protected non-bootstrap connection records, never database
names or prefixes. It continues after each failure, reports dropped, absent, detached, and
failed items, and returns non-zero while any matching record remains incomplete. Bootstrap
records and other sessions are never selected. Regular connection delete refuses
allow-drop=true records and directs callers to e2e drop or e2e cleanup; it permits
allow-drop=false records.
External value references
Connection profiles support external values for server, database, SQL username, SQL password, and a complete connection string. This lets a writable store retain stable configuration while CI or container secrets remain in environment variables or mounted files.
Existing JSON strings remain literal values, so older stores need no migration. A value can instead be a tagged reference object.
Environment variables
{
"Source": "EnvironmentVariable",
"Name": "TQ_E2E_SQL_SERVER"
}
The named variable is read only when an effective connection string is built. A missing or empty required value fails resolution without exposing another resolved value.
Whole files
{
"Source": "File",
"Path": "/run/secrets/sql-password",
"Format": "Text"
}
Text files are read whole as UTF-8 and are not trimmed. A trailing newline is part of the value.
Keyed JSON files
{
"Source": "File",
"Path": "/run/secrets/sql-auth.json",
"Format": "Json",
"Key": "username"
}
The file must contain a top-level JSON object. Key is an exact, case-sensitive
top-level property and its value must be a JSON string. Missing files, malformed JSON,
missing keys, non-string values, and unknown source shapes fail cleanly.
CLI reference boundary
The add, edit, and add-e2e-bootstrap commands accept:
--server-reference <json>
--database-reference <json>
--username-reference <json>
--password-reference <json>
--connection-string-reference <json>
Reference options require an object; a JSON string literal is rejected so they cannot be
used to smuggle plaintext secrets onto the command line. Sensitive password and access
token keys are also rejected through --opt.
Operational PowerShell examples are kept in TigerSqlCmd E2E scenarios.
Profiles operate in exactly one mode:
- full connection-string mode uses
ConnectionStringor its reference; - field mode uses server, database, authentication, username/password, encryption, and related fields.
Mixing a full connection string with individual fields is rejected before the store is changed. The CLI and validator do not choose precedence.
References are resolved lazily into an in-memory effective connection string. Copy and edit preserve the reference objects. Resolved values are never written back to the profile or store. List/show output and diagnostics display safe reference descriptions (variable name, file path, and optional key), never read the source, and redact literal passwords and complete connection strings. Locators themselves are visible, so do not put secret material in variable names, paths, or JSON keys.
On Windows, the default literal-password protection is DPAPI scoped to the current user
and machine. A copied store does not make its encrypted password portable. For CI and
containers, prefer external references or configure an appropriate
IConnectionPasswordProtector in a library host.
Library resolution
Library consumers can select a store and authorize a bootstrap without any CLI:
using ItTiger.TigerQuery.Core;
var path = SqlServerConnectionStorePathResolver.Resolve(
new SqlServerConnectionStorePathOptions
{
ExplicitFilePath = configuredStorePath,
DefaultFilePath =
SqlServerConnectionStoreOptions.AppSpecific("Example", "tests").FilePath
});
if (!path.IsSuccess)
throw new InvalidOperationException(path.ErrorMessage);
var store = new SqlServerConnectionStore(
new SqlServerConnectionStoreOptions { FilePath = path.FilePath! });
var resolution = SqlServerE2eConnectionResolver.Resolve(
store,
new SqlServerE2eConnectionResolutionOptions
{
DefaultConnectionName = "example-e2e",
RequireDatabaseCreationPermission = true,
ValidationPolicy = SqlServerConnectionValidationPolicy.DatabaseOptional
});
if (resolution.Status == SqlServerE2eResolutionStatus.NotConfigured)
return; // The host decides whether this means skip, disable, or a setup message.
if (resolution.Status != SqlServerE2eResolutionStatus.Resolved)
throw new InvalidOperationException(string.Join(" ", resolution.Errors));
Core returns neutral status values and does not depend on xUnit. Test-framework mapping is a harness responsibility.
SqlServerE2eDatabaseLifecycle
ItTiger.TigerQuery.E2e.SqlServerE2eDatabaseLifecycle performs the destructive SQL part
only after Core has returned a resolved bootstrap. Creation rechecks the selected profile
in the same store and requires current enabled=true, bootstrap=true, and
allow-database-create=true authorization.
using ItTiger.TigerQuery.E2e;
var lifecycle = new SqlServerE2eDatabaseLifecycle(store, resolution);
var databaseName = await lifecycle.CreateDatabaseAsync(cancellationToken);
try
{
var generatedProfile = lifecycle.AddDatabaseProfile();
await lifecycle.RunSetupSqlAsync(schemaSql, cancellationToken);
// Run library calls or a tiger-sqlcmd child process with generatedProfile.Name.
await lifecycle.RunTeardownSqlAsync(teardownSql, cancellationToken);
}
finally
{
if (lifecycle.CreatedDatabaseName is not null && !lifecycle.DatabaseWasDropped)
await lifecycle.CleanupAsync(CancellationToken.None);
}
Names and host overrides
The default prefix is:
_TQ_E2E_
Creation appends a 32-character GUID-style suffix. A host may set
SqlServerE2eDatabaseLifecycleOptions.DatabasePrefix; validation keeps the generated SQL
identifier within SQL Server's 128-character limit.
The prefix is a defensive cleanup guard, not ownership evidence. Changing it should make test databases easy to recognize and scope operational permissions, but it never permits prefix-wide deletion.
Exact-instance ownership and cleanup
One lifecycle instance creates at most one database and records its exact name only after the create command succeeds. Cleanup is allowed only when:
- the supplied cleanup target exactly equals that instance's recorded live name using ordinal comparison; and
- the recorded name still starts with the configured prefix.
Another lifecycle's database, an unrecorded name, a merely prefix-matching name, and an
already dropped database are rejected before DROP DATABASE. Cleanup clears idle pooled
connections for the exact database, issues one exact-name drop through master, and
removes an optional copied profile only after the database drop succeeds.
If drop fails or is cancelled, SqlServerE2eDatabaseCleanupException.DatabaseName
identifies the exact database that may remain. Ownership state and any generated profile
are retained so the same cleanup can be retried. Active connections are not forcibly
adopted or killed; they can make this drop fail. That is the deliberate difference between
this in-process lifecycle and the durable session lifecycle behind e2e drop and
e2e cleanup: ownership here lives only in the instance, so it cannot survive the process
to authorize a stronger teardown later, while a session record on disk can and does.
AddDatabaseProfile uses Core's same-store copy path, preserving protected values and
external references while setting the created database as the catalog. A full
connection-string profile can run lifecycle SQL in memory, but cannot be persisted as a
database-specific copied profile without violating full-string/field separation or
writing resolved plaintext. In that case, use the lifecycle SQL methods directly.
Orphan reporting only
DetectOrphansAsync queries names with the configured prefix, excludes the current
instance's exact database, removes duplicates, sorts ordinally, and returns an
SqlServerE2eOrphanReport. The report is informational. There is no orphan-delete or
sweep API, no age-based cleanup, and no automatic deletion.
An operator must inspect an orphan candidate, establish ownership independently, and use separate administration tooling with explicit approval. Prefix, apparent age, and absence of a running test are not sufficient evidence.
Mixed library and CLI workflow
The supported mixed-mode pattern is:
- Core selects the store and resolves the authorized bootstrap.
SqlServerE2eDatabaseLifecyclecreates one database and copies a named profile in the same store.- A library test or child
tiger-sqlcmdprocess uses that generated profile. - The lifecycle runs teardown and drops its exact database.
- Only after a successful drop does it delete the generated profile.
The repository's TigerSqlCmdE2eWorkflowLiveTests exercises this complete external
process path, including external-value references, secret redaction, report-only orphan
detection, rejection of cross-instance cleanup, a deliberately blocked cleanup, and a
successful retry.
Repository live-test behavior
The repository harness supplies
TigerSqlCmdApp.DefaultE2eBootstrapConnectionName (tiger-sqlcmd-e2e) as the host default
and normally requires database creation. A usable bootstrap therefore has all three exact
entries:
ittiger.e2e.enabled=true
ittiger.e2e.bootstrap=true
ittiger.e2e.allow-database-create=true
When the expected default bootstrap is missing, Core returns NotConfigured and the
xUnit harness calls Assert.Skip at runtime before opening a SQL connection. Invalid
and Ambiguous call Assert.Fail; bad configuration must not masquerade as an optional
environment.
Live tests require normal host execution because they need SQL Server access, normal
filesystem and user-profile access, child-process execution for tiger-sqlcmd, named
pipes/tooling processes, and access to the selected store. Sandbox-only TLS,
File.Replace, named-pipe, or child-process failures must be rerun with normal host access
before being treated as product defects.
Validated default-store mode
Remove-Item Env:TIGERQUERY_CONNECTION_STORE_FILE -ErrorAction Ignore
dotnet test ItTiger.TigerQuery.Tests\ItTiger.TigerQuery.Tests.csproj `
--filter "FullyQualifiedName~ItTiger.TigerQuery.Tests.Live" `
--no-restore --no-build --logger "console;verbosity=normal" -m:1 /nodeReuse:false
This proves that the regular tiger-sqlcmd application-default store and expected name
are sufficient; no special E2E enable environment variable exists.
Validated environment-selected mode
Prepare an isolated store containing its own tiger-sqlcmd-e2e bootstrap, then run:
$env:TIGERQUERY_CONNECTION_STORE_FILE = 'C:\temp\tigerquery-e2e.json'
dotnet test ItTiger.TigerQuery.Tests\ItTiger.TigerQuery.Tests.csproj `
--filter "FullyQualifiedName~ItTiger.TigerQuery.Tests.Live" `
--no-restore --no-build --logger "console;verbosity=normal" -m:1 /nodeReuse:false
Remove-Item Env:TIGERQUERY_CONNECTION_STORE_FILE -ErrorAction Ignore
This proves the environment override outranks the application default and remains only a path selector. Always remove the process override after a run so later commands do not silently remain attached to the alternate store.
Unconfigured safety gate
The full suite is also run against a unique missing store and verifies that resolution does not create it:
$unconfiguredStore = Join-Path $env:TEMP (Join-Path `
('TigerQuery-unconfigured-' + [Guid]::NewGuid().ToString('N')) `
'connections.json')
$env:TIGERQUERY_CONNECTION_STORE_FILE = $unconfiguredStore
dotnet test TigerQuery.sln --no-restore --no-build `
--logger "console;verbosity=normal" -m:1 /nodeReuse:false
$testExit = $LASTEXITCODE
$storeCreated = Test-Path -LiteralPath $unconfiguredStore
Remove-Item Env:TIGERQUERY_CONNECTION_STORE_FILE -ErrorAction Ignore
"UnconfiguredStoreCreated=$storeCreated"
exit $testExit
The expected result is UnconfiguredStoreCreated=False. An unconfigured skip proves
only that the selected store had no usable bootstrap and no SQL work occurred; it does
not prove that the environment variable was absent.
Operational guidance by environment
Local development
- Prefer the regular shared store and the default
tiger-sqlcmd-e2ename. - Grant
--allow-database-createonly on a dedicated test SQL Server or principal. - Leave
TIGERQUERY_CONNECTION_STORE_FILEunset except for intentional isolation. - Treat reported orphan names as manual investigation items.
CI
- Use a job-owned writable store selected by
TIGERQUERY_CONNECTION_STORE_FILE. - Create or provision the exact expected bootstrap in that store.
- Supply SQL credentials through environment or mounted-file references, not literal argv values.
- Publish logs; cleanup exceptions name the exact database that may require attention.
- Run the unconfigured gate separately from the authorized live workflow.
Containers
- Mount the store and referenced secret files at paths visible inside the container.
- Do not copy a developer's DPAPI-protected store and expect it to decrypt.
- Use an explicitly addressable server supplied in the profile; do not add endpoint discovery as a convenience fallback.
- Give the SQL principal only the permissions required in a dedicated E2E environment.
Library hosts
- Define the application default and expected bootstrap name explicitly.
- Use Core's path and E2E resolvers; do not pre-merge values in a way that loses their different failure semantics.
- Map
NotConfiguredaccording to host policy and treatInvalid/Ambiguousas faults. - Inject
SqlServerExternalValueResolutionOptionswhen tests need controlled environment or file readers.
Package ownership boundaries
| Component | Owns |
|---|---|
ItTiger.TigerQuery.Core |
Connection profiles and stores, path precedence, external values, validation/redaction, reserved E2E metadata, neutral bootstrap resolution, and same-store profile copying. It opens no SQL connection during E2E resolution. |
ItTiger.TigerQuery.CliCore |
Reusable TigerCli connection commands, TigerQueryCliContribution, the standard store option/environment help, --e2e, add-e2e-bootstrap, external-reference CLI binding, and semantic CLI outcomes. |
ItTiger.TigerQuery |
SQLCMD execution and the SQL-backed SqlServerE2eDatabaseLifecycle, exact ownership, setup/teardown execution, cleanup exceptions, and report-only orphan detection. |
tiger-sqlcmd |
The executable host: shared-store default, tiger-sqlcmd-e2e convention, composition of Core/CliCore/engine, application exit codes, console UI, and child-process workflows. |
These boundaries matter. Core authorization remains useful without TigerCli or xUnit; CliCore does not need the script engine; the lifecycle reuses Core authorization instead of inventing a second connection mechanism; and the executable supplies host conventions without turning them into universal library defaults.
Security and lifecycle rationale
- Explicit store selection prevents lateral fallback. A malformed CI override cannot redirect work into a developer's personal default store.
- Name plus metadata separates identity from authority. The name prevents selection drift; reserved metadata records deliberate authorization. Requiring both prevents an ordinary profile from becoming destructive merely because it has a familiar name.
- Database creation is a separate grant. General E2E permission does not imply schema or database creation rights.
- No probing keeps absence safe. A fresh machine remains inert even if SQL Server is reachable locally.
- Exact ownership prevents prefix-based deletion. Prefixes help humans and add a second guard, but only the lifecycle's successful create record authorizes cleanup.
- Retryable cleanup preserves evidence. A failed drop retains the exact name and copied profile instead of concealing or broadening the failure.
- Force follows proof, never precedes it. A session-owned database is torn down with a forced rollback because its ownership was already established by an exact record, not in order to establish it. No amount of force is applied to anything a record does not name.
- References stay references. Resolving only in memory lets automation use rotating secrets without persisting them or exposing them through inspection.
- Orphans are reported, not swept. Automated code cannot prove that a similarly named database is abandoned or belongs to the current job.
Known limitations
- The repository live workflow requires a SQL principal that can create and drop
databases when
allow-database-create=trueis required. - SQL-authentication profiles intentionally skip tests that specifically require Windows integrated authentication. This is a coverage limitation, not an E2E-resolution failure; SQL-auth-compatible live tests can still run.
- DPAPI-protected literal passwords are bound to the Windows user and machine. Use external references or a host-selected protector for portable automation.
- The forced single-user teardown belongs to the durable session lifecycle only. The
in-process
SqlServerE2eDatabaseLifecyclestill fails its drop when something else is connected to the database it created. - A full connection-string profile is retargeted through a persisted initial-catalog override when TigerQuery creates a per-database copy. The referenced connection string remains unresolved and is never written back.
- The lifecycle does not force-close active sessions. Cleanup can fail and must be retried or investigated using the exact reported database name.
- Orphan detection is read-only and intentionally provides no automatic cleanup.
- The default
_TQ_E2E_prefix is recognizable but is not a security boundary or proof of ownership. - E2E resolution validates configuration without testing reachability or SQL permissions; those failures occur only when the authorized workflow performs SQL.