1 //! f0rge #70: gl0w branding + gl0w.world attribution on every page,2 //! theme-aware logo, and an og:image social card.3 4 use std::path::{Path, PathBuf};5 use std::process::Command;6 7 fn fresh(prefix: &str) -> PathBuf {8 let dir = std::env::temp_dir().join(format!(9 "hu13-brand-{prefix}-{}-{}",10 std::process::id(),11 std::time::SystemTime::now()12 .duration_since(std::time::UNIX_EPOCH)13 .map(|d| d.as_nanos())14 .unwrap_or(0)15 ));16 std::fs::create_dir_all(&dir).unwrap();17 dir18 }19 20 fn git(repo: &Path, args: &[&str]) {21 let st = Command::new("git")22 .arg("-C")23 .arg(repo)24 .args(args)25 .status()26 .expect("spawn git");27 assert!(st.success(), "git {args:?}");28 }29 30 fn build() -> PathBuf {31 let repo = fresh("repo");32 let out = fresh("out");33 git(&repo, &["init", "-b", "main"]);34 git(&repo, &["config", "user.name", "hu13-test"]);35 git(&repo, &["config", "user.email", "[email protected]"]);36 git(&repo, &["config", "commit.gpgsign", "false"]);37 std::fs::write(repo.join("README.md"), "# brand\n").unwrap();38 git(&repo, &["add", "-A"]);39 git(&repo, &["commit", "-m", "brand"]);40 let produced = Command::new(env!("CARGO_BIN_EXE_hu13"))41 .arg("build")42 .arg(&repo)43 .arg("-o")44 .arg(&out)45 .output()46 .unwrap();47 assert!(48 produced.status.success(),49 "{}",50 String::from_utf8_lossy(&produced.stderr)51 );52 out53 }54 55 #[test]56 fn header_and_footer_carry_gl0w_branding() {57 let out = build();58 for page in ["index.html", "agents.html", "issues/index.html"] {59 let html = std::fs::read_to_string(out.join(page)).expect(page);60 assert!(61 html.contains("<span class=\"gl0w-mark\" aria-hidden=\"true\"><svg"),62 "{page}: gl0w logo mark in header"63 );64 assert!(65 html.contains("viewBox=\"0 0 357 306\"") && html.contains("M2,2 L355,2"),66 "{page}: gf1 gl0w mark geometry"67 );68 assert!(69 !html.contains("<span class=\"gl0w-mark\" aria-hidden=\"true\">gl0w</span>"),70 "{page}: text placeholder replaced by logo"71 );72 assert!(73 html.contains("<a class=\"gl0w-mark\" href=\"https://gl0w.world\" rel=\"noopener\" aria-label=\"gl0w\">"),74 "{page}: footer logo links gl0w.world"75 );76 assert!(77 html.contains("hu13 — a gl0w project"),78 "{page}: brand tagline"79 );80 assert!(81 html.contains("href=\"https://gl0w.world\""),82 "{page}: links to gl0w.world"83 );84 assert!(85 html.contains("footer-provenance"),86 "{page}: provenance kept"87 );88 // Header lockup keeps the hu13 mark and is linked home.89 assert!(90 html.contains("<img class=\"site-logo\" src=\"/logo.svg\" alt=\"hu13 logo\""),91 "{page}: hu13 logo"92 );93 assert!(94 html.contains("href=\"/\" aria-label=\"hu13 by gl0w"),95 "{page}: lockup links home"96 );97 }98 }99 100 #[test]101 fn social_card_and_meta_present() {102 let out = build();103 let html = std::fs::read_to_string(out.join("index.html")).unwrap();104 for needle in [105 "<meta property=\"og:image\" content=\"/og.png\">",106 "<meta property=\"og:image:width\" content=\"1200\">",107 "<meta property=\"og:image:height\" content=\"630\">",108 "<meta name=\"twitter:card\" content=\"summary_large_image\">",109 "<meta property=\"og:site_name\" content=\"hu13 by gl0w\">",110 ] {111 assert!(html.contains(needle), "index.html has {needle}");112 }113 let png = std::fs::read(out.join("og.png")).expect("og.png emitted");114 assert!(png.starts_with(b"\x89PNG\r\n\x1a\n"), "real PNG");115 assert!(png.len() > 1000, "not a stub");116 }117 118 #[test]119 fn logo_has_no_baked_background() {120 let out = build();121 let logo =122 std::fs::read_to_string(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("static/logo.svg"))123 .unwrap();124 let gl0w =125 std::fs::read_to_string(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("static/gl0w.svg"))126 .unwrap();127 assert!(128 gl0w.contains("viewBox=\"0 0 357 306\"") && gl0w.contains("fill=\"currentColor\""),129 "vendored gf1 gl0w mark"130 );131 let built = std::fs::read_to_string(out.join("gl0w.svg")).unwrap();132 assert_eq!(built, gl0w, "build emits the same gl0w logo");133 assert!(!logo.contains("fill=\"#1a1b26\""), "no dark slab");134 assert!(135 logo.contains("@media (prefers-color-scheme:light)"),136 "ink flips for light theme"137 );138 let css =139 std::fs::read_to_string(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("static/style.css"))140 .unwrap();141 assert!(142 css.contains(".site-logo{display:block;width:auto;height:1.75rem"),143 "logo keeps its 3:1 aspect (was squeezed into 48x48)"144 );145 }146 147 #[test]148 fn repository_readme_has_no_ascii_logo_or_clone_duplicate() {149 let readme = std::fs::read_to_string(150 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".forge/wiki/README"),151 )152 .unwrap();153 assert!(154 !readme.contains("███") && !readme.contains("▓▓"),155 "README carries no ASCII-art logo"156 );157 assert!(158 !readme.contains("## Clone it"),159 "clone/download lives in the code header, not a duplicate section"160 );161 assert!(162 !readme.starts_with("# "),163 "README does not repeat the repo title; the page header already carries it"164 );165 assert!(166 readme.starts_with("*hu13 (pronounced"),167 "README opens with its intro"168 );169 assert!(readme.contains("## Built with"), "README keeps its ending");170 }