use onetaskgraph_github_projects::{DETAIL_BATCH, graphql, worst_case_point_cost};
const PRICES: &[(&str, u64)] = &[
(graphql::SEARCH_ISSUES, 5),
(graphql::ISSUE, 1),
(graphql::ISSUE_BOARD_ITEMS, 1),
(graphql::SUB_ISSUES, 5),
(graphql::BOARD, 2),
(graphql::ORIGIN_LOOKUP, 1),
(graphql::BOARD_FIELDS, 1),
(graphql::DRAFT, 1),
(graphql::REPOSITORY, 1),
(graphql::CREATION_CONTEXT, 1),
(graphql::ISSUE_DEPENDENCIES, 1),
(graphql::CREATE_ISSUE, 1),
(graphql::ADD_TO_BOARD, 1),
(graphql::UPDATE_ISSUE, 1),
(graphql::UPDATE_DRAFT, 1),
(graphql::UPDATE_FIELD, 1),
(graphql::UPDATE_FIELDS, 1),
(graphql::CLEAR_FIELD, 1),
(graphql::CREATE_FIELD, 1),
(graphql::ADD_SUB_ISSUE, 1),
(graphql::REMOVE_SUB_ISSUE, 1),
(graphql::ADD_BLOCKED_BY, 1),
(graphql::REMOVE_BLOCKED_BY, 1),
(graphql::DELETE_ISSUE, 1),
(graphql::ISSUE_COMMENTS, 1),
(graphql::ISSUE_DETAIL, 1),
(graphql::ISSUE_DETAILS, 1),
(graphql::COMMENT_ISSUE, 1),
(graphql::ADD_COMMENT, 1),
(graphql::UPDATE_COMMENT, 1),
(graphql::DELETE_COMMENT, 1),
(graphql::STATUS_OPTIONS_SNAPSHOT, 1),
(graphql::STATUS_OPTIONS_UPDATE, 1),
];
fn mispriced(doing: &str, document: &str, recorded: Option<u64>) -> Option<String> {
let price = worst_case_point_cost(document)
.unwrap_or_else(|error| panic!("the document for {doing} could not be priced: {error}"));
match recorded {
None => Some(format!(
"the document for {doing} costs {price} points and this file records no price \
for it; next: add it to PRICES at {price}"
)),
Some(recorded) if recorded != price => Some(format!(
"the document for {doing} now costs {price} points and this file records \
{recorded}; next: if the change is deliberate, put {price} in PRICES and say \
in session-cost.md what moved it"
)),
Some(_) => None,
}
}
fn recorded(document: &str) -> Option<u64> {
PRICES
.iter()
.find(|(pinned, _)| *pinned == document)
.map(|(_, price)| *price)
}
#[test]
fn every_document_this_source_sends_costs_what_this_file_records() {
let moved = graphql::DOCUMENTS
.iter()
.filter_map(|(document, doing)| mispriced(doing, document, recorded(document)))
.collect::<Vec<_>>();
assert!(moved.is_empty(), "{}", moved.join("\n"));
}
#[test]
fn every_price_recorded_here_belongs_to_a_document_this_source_sends() {
let orphaned = PRICES
.iter()
.filter(|(pinned, _)| {
!graphql::DOCUMENTS
.iter()
.any(|(document, _)| document == pinned)
})
.map(|(pinned, price)| {
format!("this file records {price} points for a document the inventory does not hold; next: delete the entry, or put the document back in graphql::DOCUMENTS:\n{pinned}")
})
.collect::<Vec<_>>();
assert!(orphaned.is_empty(), "{}", orphaned.join("\n"));
}
#[test]
fn the_check_reports_a_failure_naming_a_document_whose_price_moved() {
let refusal = mispriced("reading the board", graphql::BOARD, Some(8))
.expect("a price this document does not cost is refused");
assert!(refusal.contains("reading the board"), "{refusal}");
assert!(refusal.contains('8'), "{refusal}");
assert!(refusal.contains('2'), "{refusal}");
}
#[test]
fn the_check_reports_a_failure_naming_a_document_nobody_priced() {
let refusal = mispriced("reading the board", graphql::BOARD, None)
.expect("a document with no recorded price is refused");
assert!(refusal.contains("reading the board"), "{refusal}");
assert!(refusal.contains("records no price"), "{refusal}");
}
#[test]
fn the_batch_read_is_the_largest_one_point_buys() {
const {
assert!(
DETAIL_BATCH >= 10,
"a batch this small is not worth a document"
)
};
let document = graphql::ISSUE_DETAILS;
assert!(!document.contains("nodes(ids:"), "{document}");
assert_eq!(document.matches(":node(id:$id").count(), DETAIL_BATCH);
assert_eq!(worst_case_point_cost(document).unwrap(), 1);
let last = DETAIL_BATCH - 1;
let alias_at = document
.find(&format!("i{last}:node(id:$id{last})"))
.expect("the last alias");
let alias_end = document[alias_at..]
.find("\n")
.map_or(document.len(), |end| alias_at + end);
let alias = &document[alias_at..alias_end];
let grown_alias = alias
.replacen(&format!("i{last}:"), &format!("i{DETAIL_BATCH}:"), 1)
.replacen(&format!("$id{last}"), &format!("$id{DETAIL_BATCH}"), 1);
let grown = format!(
"{}\n {grown_alias}{}",
&document[..alias_end],
&document[alias_end..]
)
.replacen(
&format!("$id{last}:ID!,"),
&format!("$id{last}:ID!,$id{DETAIL_BATCH}:ID!,"),
1,
);
assert_eq!(grown.matches(":node(id:$id").count(), DETAIL_BATCH + 1);
assert_eq!(
worst_case_point_cost(&grown).unwrap(),
2,
"one item more than DETAIL_BATCH is still one point, so DETAIL_BATCH is not the \
largest batch one point buys:\n{grown}"
);
}