| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
use super::GateCtx; |
| 8 |
use super::log::GateLog; |
| 9 |
use crate::domain::{GateKind, GateRunId, TierId}; |
| 10 |
use crate::events; |
| 11 |
use chrono::Utc; |
| 12 |
use sqlx::SqlitePool; |
| 13 |
use sqlx::sqlite::SqlitePoolOptions; |
| 14 |
use std::collections::HashMap; |
| 15 |
use std::path::PathBuf; |
| 16 |
|
| 17 |
pub(super) fn target(dir: &str) -> crate::config::TestTarget { |
| 18 |
crate::config::TestTarget { |
| 19 |
dir: std::path::PathBuf::from(dir), |
| 20 |
aux_repo: None, |
| 21 |
features: Vec::new(), |
| 22 |
all_features: false, |
| 23 |
scratch_db: false, |
| 24 |
} |
| 25 |
} |
| 26 |
|
| 27 |
|
| 28 |
pub(super) fn aux_target(dir: &str, repo: &str) -> crate::config::TestTarget { |
| 29 |
crate::config::TestTarget { |
| 30 |
aux_repo: Some(repo.to_string()), |
| 31 |
..target(dir) |
| 32 |
} |
| 33 |
} |
| 34 |
|
| 35 |
|
| 36 |
|
| 37 |
pub(super) fn url_host_is_a_domain(url: &str) -> bool { |
| 38 |
let after = url.split("://").nth(1).unwrap_or(""); |
| 39 |
let host = after.split(['/', '?', '#']).next().unwrap_or(""); |
| 40 |
let host = host.rsplit('@').next().unwrap_or(host); |
| 41 |
let host = if let Some(rest) = host.strip_prefix('[') { |
| 42 |
rest.split(']').next().unwrap_or("") |
| 43 |
} else { |
| 44 |
host.split(':').next().unwrap_or("") |
| 45 |
}; |
| 46 |
!host.is_empty() && host.parse::<std::net::IpAddr>().is_err() |
| 47 |
} |
| 48 |
|
| 49 |
pub(super) fn resolving_ctx(worktree: &str, aux: &[(&str, &str)]) -> GateCtx { |
| 50 |
GateCtx { |
| 51 |
public_url: None, |
| 52 |
pool: SqlitePool::connect_lazy("sqlite::memory:").unwrap(), |
| 53 |
cfg: std::sync::Arc::new(crate::config::AppConfig::for_tests()), |
| 54 |
tier: TierId::new("host"), |
| 55 |
version: "0.1.0".parse().unwrap(), |
| 56 |
worktree: Some(PathBuf::from(worktree)), |
| 57 |
bundle: None, |
| 58 |
events: events::channel(), |
| 59 |
nodes: Vec::new(), |
| 60 |
build_id: None, |
| 61 |
aux_dirs: aux |
| 62 |
.iter() |
| 63 |
.map(|(n, d)| ((*n).to_string(), PathBuf::from(d))) |
| 64 |
.collect(), |
| 65 |
} |
| 66 |
} |
| 67 |
|
| 68 |
|
| 69 |
|
| 70 |
pub(super) async fn frontend_ctx(worktree: &std::path::Path, dirs: &[&str]) -> GateCtx { |
| 71 |
let mut cfg = crate::config::AppConfig::for_tests(); |
| 72 |
cfg.frontend_builds = dirs |
| 73 |
.iter() |
| 74 |
.map(|d| crate::config::FrontendBuild { |
| 75 |
dir: PathBuf::from(d), |
| 76 |
script: "build".into(), |
| 77 |
}) |
| 78 |
.collect(); |
| 79 |
cfg.logs_root = worktree.join("logs"); |
| 80 |
GateCtx { |
| 81 |
public_url: None, |
| 82 |
pool: SqlitePoolOptions::new() |
| 83 |
.max_connections(1) |
| 84 |
.connect("sqlite::memory:") |
| 85 |
.await |
| 86 |
.unwrap(), |
| 87 |
cfg: std::sync::Arc::new(cfg), |
| 88 |
tier: TierId::new("host"), |
| 89 |
version: "0.1.0".parse().unwrap(), |
| 90 |
worktree: Some(worktree.to_path_buf()), |
| 91 |
bundle: None, |
| 92 |
events: events::channel(), |
| 93 |
nodes: Vec::new(), |
| 94 |
build_id: None, |
| 95 |
aux_dirs: HashMap::new(), |
| 96 |
} |
| 97 |
} |
| 98 |
|
| 99 |
|
| 100 |
|
| 101 |
|
| 102 |
|
| 103 |
pub(super) async fn dry_run_ctx(worktree: &std::path::Path, max_age_hours: u32) -> GateCtx { |
| 104 |
let mut cfg = crate::config::AppConfig::for_tests(); |
| 105 |
cfg.scratch_db_url = Some("postgres:///sando_scratch".into()); |
| 106 |
cfg.backup_max_age_hours = max_age_hours; |
| 107 |
cfg.logs_root = worktree.join("logs"); |
| 108 |
let pool = SqlitePoolOptions::new() |
| 109 |
.max_connections(1) |
| 110 |
.connect("sqlite::memory:") |
| 111 |
.await |
| 112 |
.unwrap(); |
| 113 |
crate::db::migrate(&pool).await.unwrap(); |
| 114 |
GateCtx { |
| 115 |
public_url: None, |
| 116 |
pool, |
| 117 |
cfg: std::sync::Arc::new(cfg), |
| 118 |
tier: TierId::new("host"), |
| 119 |
version: "0.1.0".parse().unwrap(), |
| 120 |
worktree: Some(worktree.to_path_buf()), |
| 121 |
bundle: None, |
| 122 |
events: events::channel(), |
| 123 |
nodes: Vec::new(), |
| 124 |
build_id: None, |
| 125 |
aux_dirs: HashMap::new(), |
| 126 |
} |
| 127 |
} |
| 128 |
|
| 129 |
|
| 130 |
pub(super) async fn seed_backup(ctx: &GateCtx, hours_ago: i64) { |
| 131 |
seed_named_backup(ctx, "server", hours_ago).await; |
| 132 |
} |
| 133 |
|
| 134 |
|
| 135 |
pub(super) async fn seed_named_backup(ctx: &GateCtx, name: &str, hours_ago: i64) { |
| 136 |
let at = (Utc::now() - chrono::Duration::hours(hours_ago)).to_rfc3339(); |
| 137 |
sqlx::query( |
| 138 |
"INSERT INTO backups (name, fetched_at, source, local_path, byte_size) |
| 139 |
VALUES (?, ?, 'file:///x.sql.gz', '/tmp/sando-test-backup.sql.gz', 1000000)", |
| 140 |
) |
| 141 |
.bind(name) |
| 142 |
.bind(at) |
| 143 |
.execute(&ctx.pool) |
| 144 |
.await |
| 145 |
.unwrap(); |
| 146 |
} |
| 147 |
|
| 148 |
|
| 149 |
pub(super) fn mt_check() -> crate::config::MigrationCheck { |
| 150 |
crate::config::MigrationCheck { |
| 151 |
dir: std::path::PathBuf::from("multithreaded/migrations"), |
| 152 |
backup: "multithreaded".into(), |
| 153 |
scratch_db: Some("sando_scratch_mt".into()), |
| 154 |
owner_role: Some("multithreaded".into()), |
| 155 |
} |
| 156 |
} |
| 157 |
|
| 158 |
|
| 159 |
pub(super) fn with_check(ctx: &mut GateCtx, check: crate::config::MigrationCheck) { |
| 160 |
let mut cfg = crate::config::AppConfig::for_tests(); |
| 161 |
cfg.scratch_db_url = ctx.cfg.scratch_db_url.clone(); |
| 162 |
cfg.backup_max_age_hours = ctx.cfg.backup_max_age_hours; |
| 163 |
cfg.logs_root = ctx.cfg.logs_root.clone(); |
| 164 |
cfg.migration_checks = vec![check]; |
| 165 |
ctx.cfg = std::sync::Arc::new(cfg); |
| 166 |
} |
| 167 |
|
| 168 |
|
| 169 |
|
| 170 |
|
| 171 |
pub(super) async fn test_gate_log(ctx: &GateCtx) -> GateLog { |
| 172 |
GateLog::open(ctx, GateRunId(0), GateKind::CodeSmoke).await |
| 173 |
} |
| 174 |
|
| 175 |
|
| 176 |
pub(super) async fn read_gate_log(ctx: &GateCtx, log: GateLog) -> String { |
| 177 |
log.close().await; |
| 178 |
tokio::fs::read_to_string(ctx.log_path(GateKind::CodeSmoke)) |
| 179 |
.await |
| 180 |
.expect("the gate log must exist on disk") |
| 181 |
} |
| 182 |
|