use super::*;
#[test]
fn file_row_mapping_preserves_projection_identity_and_version() {
let connection = Connection::open_in_memory().expect("database should open");
let file = connection
.query_row(
"
SELECT 'software-file', 'repository', 'scope', 'src/lib.rs', 'rust',
'source', 'parsed', 7
",
[],
file_from_row,
)
.expect("software file should decode");
assert_eq!(file.software_file_id, "software-file");
assert_eq!(file.path, "src/lib.rs");
assert_eq!(file.file_role, "source");
assert_eq!(file.created_graph_version, GraphVersion::new(7));
}
#[test]
fn file_page_assigns_owned_roles_before_domain_validation() {
let connection = Connection::open_in_memory().expect("database should open");
connection
.execute_batch(
"
CREATE TABLE code_repository_files (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
language_id TEXT NOT NULL,
parse_status TEXT NOT NULL
);
CREATE TABLE code_repository_symbols (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
name TEXT NOT NULL,
kind TEXT NOT NULL,
line_start INTEGER NOT NULL,
line_end INTEGER NOT NULL
);
INSERT INTO code_repository_files VALUES
('repository', 'scope', 'Cargo.toml', 'toml', 'parsed'),
('repository', 'other', 'src/lib.rs', 'rust', 'parsed');
",
)
.expect("file source schema should initialize");
let files = software_file_page(&connection, "scope", GraphVersion::new(3), 10, None)
.expect("software files should load");
assert_eq!(files.len(), 1);
assert_eq!(files[0].file_role, "dependency_manifest");
assert_eq!(files[0].created_graph_version, GraphVersion::new(3));
}
#[test]
fn file_page_authorizes_current_and_legacy_map_topic_shards() {
let connection = Connection::open_in_memory().expect("database should open");
connection
.execute_batch(
"
CREATE TABLE code_repository_files (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
language_id TEXT NOT NULL,
parse_status TEXT NOT NULL
);
CREATE TABLE code_repository_symbols (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
name TEXT NOT NULL,
kind TEXT NOT NULL,
line_start INTEGER NOT NULL
);
",
)
.expect("software source schema should initialize");
for (line, root, shard, topic, identity) in [
(
5,
KNOWLEDGE_MAP_RELATIVE_PATH,
"knowledge/topics/current.yaml",
"Current",
"current-identity",
),
(
6,
LEGACY_KNOWLEDGE_MAP_RELATIVE_PATH,
".knowledge/topics/legacy.yaml",
"Legacy",
"legacy-identity",
),
] {
connection
.execute(
"INSERT INTO code_repository_files VALUES ('repository', 'scope', ?1, 'yaml', 'parsed')",
params![shard],
)
.expect("topic shard file should seed");
connection
.execute_batch(&format!(
"
INSERT INTO code_repository_symbols VALUES
('repository', 'scope', '{root}', '{shard}', 'knowledge_map_topic_shard_ref', {line}),
('repository', 'scope', '{root}', '{topic}', 'knowledge_map_topic_shard_topic', {line}),
('repository', 'scope', '{root}', '{identity}', 'knowledge_map_topic_shard_identity', {line}),
('repository', 'scope', '{shard}', '{topic}', 'knowledge_map_topic_shard', 3),
('repository', 'scope', '{shard}', '{identity}', 'knowledge_map_topic_shard_identity', 3);
"
))
.expect("topic shard symbols should seed");
}
let files = software_file_page(&connection, "scope", GraphVersion::new(1), 10, None)
.expect("software files should load");
assert_eq!(files.len(), 2);
assert!(
files
.iter()
.all(|file| file.file_role == "knowledge_map_topic_shard")
);
}
#[test]
fn file_projection_reuses_one_insert_prepare_across_keyset_pages() {
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use rusqlite::hooks::{AuthAction, AuthContext, Authorization};
const FILE_COUNT: usize = 1_025;
let connection = Connection::open_in_memory().expect("database should open");
connection
.execute_batch(
"
CREATE TABLE code_repository_files (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
language_id TEXT NOT NULL,
parse_status TEXT NOT NULL,
PRIMARY KEY (source_scope, path)
);
CREATE TABLE code_repository_symbols (
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
name TEXT NOT NULL,
kind TEXT NOT NULL,
line_start INTEGER NOT NULL,
line_end INTEGER NOT NULL
);
CREATE TABLE software_files (
software_file_id TEXT PRIMARY KEY,
repository_id TEXT NOT NULL,
source_scope TEXT NOT NULL,
path TEXT NOT NULL,
language_id TEXT NOT NULL,
file_role TEXT NOT NULL,
parse_status TEXT NOT NULL,
created_graph_version INTEGER NOT NULL
);
WITH RECURSIVE file_number(value) AS (
VALUES (0)
UNION ALL
SELECT value + 1 FROM file_number WHERE value < 1024
)
INSERT INTO code_repository_files (
repository_id, source_scope, path, language_id, parse_status
)
SELECT 'repository', 'scope', printf('src/file-%04d.rs', value), 'rust', 'parsed'
FROM file_number;
",
)
.expect("file projection schema and source rows should initialize");
let insert_prepare_count = Arc::new(AtomicUsize::new(0));
let observed_insert_prepares = Arc::clone(&insert_prepare_count);
connection.authorizer(Some(move |context: AuthContext<'_>| {
if matches!(
context.action,
AuthAction::Insert {
table_name: "software_files"
}
) {
observed_insert_prepares.fetch_add(1, Ordering::Relaxed);
}
Authorization::Allow
}));
let projected = materialize_files(&connection, "scope", GraphVersion::new(9))
.expect("software files should materialize");
let rows = connection
.prepare(
"
SELECT path, file_role, parse_status, created_graph_version
FROM software_files
WHERE source_scope = 'scope'
ORDER BY path ASC
",
)
.expect("projected files should be queryable")
.query_map([], |row| {
Ok((
row.get::<_, String>(0)?,
row.get::<_, String>(1)?,
row.get::<_, String>(2)?,
row.get::<_, u64>(3)?,
))
})
.expect("projected file rows should decode")
.collect::<Result<Vec<_>, _>>()
.expect("projected file rows should collect");
let expected = (0..FILE_COUNT)
.map(|value| {
(
format!("src/file-{value:04}.rs"),
"source".to_owned(),
"parsed".to_owned(),
9_u64,
)
})
.collect::<Vec<_>>();
let distinct_ids = connection
.query_row(
"SELECT COUNT(DISTINCT software_file_id) FROM software_files WHERE source_scope = 'scope'",
[],
|row| row.get::<_, usize>(0),
)
.expect("projected software file ids should count");
assert_eq!(projected, FILE_COUNT);
assert_eq!(rows, expected);
assert_eq!(distinct_ids, FILE_COUNT);
assert_eq!(insert_prepare_count.load(Ordering::Relaxed), 1);
}