лендинг: имя боевого сервера убрано из пользовательской копии
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
«Симуляция пробы покрытия на боевой базе (poincare, 30.08.2026)» доезжала до экрана: CITY_COVERAGE_SOURCE собирается в coverage-copy.ts и рендерится в EstimateFlow.tsx:323. На первом экране лендинга строки не видно — она появляется в форме проверки, после выбора города, поэтому grep по отрендеренной главной давал ноль и дефект выглядел несуществующим. Хостнейм посетителю не сообщает ничего: «на боевой базе» несёт весь смысл. Убран из обеих строк-источников (сверка бэктеста и проба покрытия) и из комментария в статьях. Гейт смотрит в ИСХОДНИК, а не в вывод: какой путь рендера показывает строку сегодня, знать не нужно — завтра он будет другим. Список запретов явный, без эвристики «похоже на хостнейм»: она ловила бы domclick.ru и avito.ru в текстах про источники данных. gendsgn.ru в список НЕ входит — это публичный домен B2B, на который лендинг ссылается кнопкой «Для бизнеса». Гейт проверен фальсификацией: возврат хостнейма даёт красное с указанием landing-facts.ts:207, а не пустой список.
This commit is contained in:
parent
56e4de0b35
commit
00f177df03
3 changed files with 93 additions and 3 deletions
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* Инфраструктурные имена не должны попадать в файлы пользовательской копии.
|
||||||
|
*
|
||||||
|
* 30.08.2026 «Симуляция пробы покрытия на боевой базе (poincare, 30.08.2026)»
|
||||||
|
* доехала до экрана: `CITY_COVERAGE_SOURCE` собирается в `coverage-copy.ts` и
|
||||||
|
* рендерится в `EstimateFlow.tsx:323`. На первом экране лендинга её не видно —
|
||||||
|
* она появляется в форме проверки, после выбора города. Поэтому grep по
|
||||||
|
* отрендеренной главной давал ноль, и дефект выглядел несуществующим.
|
||||||
|
*
|
||||||
|
* Отсюда форма гейта: он смотрит в ИСХОДНИК, а не в вывод. Какой именно путь
|
||||||
|
* рендера сегодня показывает строку, знать не нужно — завтра он будет другим,
|
||||||
|
* а хостнейм прода посетителю не сообщает ничего ни на одном из них.
|
||||||
|
*
|
||||||
|
* Скан идёт по всему тексту файла, включая комментарии: комментарий рядом с
|
||||||
|
* копией — заготовка следующей строки копии, и «на боевой базе» в нём ничем
|
||||||
|
* не хуже имени машины.
|
||||||
|
*/
|
||||||
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const ROOT = join(__dirname, "..");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Имена машин, контейнеров и внутренних хостов. Список ЯВНЫЙ, а не эвристика:
|
||||||
|
* эвристика на «что-то похожее на хостнейм» ловила бы доменные имена в текстах
|
||||||
|
* про источники данных (domclick.ru, avito.ru), которые тут законны.
|
||||||
|
*
|
||||||
|
* `gendsgn.ru` в список НЕ входит намеренно: это публичный домен B2B, на
|
||||||
|
* который лендинг ссылается кнопкой «Для бизнеса» (`B2B_URL` в content.ts).
|
||||||
|
* Запрет на него сломал бы рабочую ссылку — граница проходит по «внутреннее
|
||||||
|
* имя машины» против «адрес, который посетитель и так увидит в браузере».
|
||||||
|
*/
|
||||||
|
const BANNED = [
|
||||||
|
"poincare",
|
||||||
|
"beget",
|
||||||
|
"tradein-postgres",
|
||||||
|
"tradein-backend",
|
||||||
|
"tradein-scraper",
|
||||||
|
"gendesign-postgres",
|
||||||
|
"gendesign-backend",
|
||||||
|
"glitchtip",
|
||||||
|
"forgejo",
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Файлы копии: то, из чего собирается видимый посетителю текст. */
|
||||||
|
function copySources(dir: string, acc: string[] = []): string[] {
|
||||||
|
for (const name of readdirSync(dir)) {
|
||||||
|
if (name === "__tests__" || name === "node_modules") continue;
|
||||||
|
const full = join(dir, name);
|
||||||
|
if (statSync(full).isDirectory()) {
|
||||||
|
copySources(full, acc);
|
||||||
|
} else if (name.endsWith(".ts") || name.endsWith(".tsx")) {
|
||||||
|
acc.push(full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("публичная копия не содержит инфраструктурных имён", () => {
|
||||||
|
const files = copySources(ROOT);
|
||||||
|
|
||||||
|
it("файлы копии вообще найдены — иначе гейт зелёный по построению", () => {
|
||||||
|
// Без этой проверки сломанный обход каталога дал бы пустой список и
|
||||||
|
// «нарушений нет» на любом содержимом.
|
||||||
|
expect(files.length).toBeGreaterThan(20);
|
||||||
|
expect(files.some((f) => f.endsWith("landing-facts.ts"))).toBe(true);
|
||||||
|
expect(files.some((f) => f.endsWith("coverage-copy.ts"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(BANNED)("нигде не встречается «%s»", (needle) => {
|
||||||
|
const hits: string[] = [];
|
||||||
|
for (const file of files) {
|
||||||
|
const text = readFileSync(file, "utf8");
|
||||||
|
text.split("\n").forEach((line, i) => {
|
||||||
|
if (line.toLowerCase().includes(needle)) {
|
||||||
|
hits.push(`${file.slice(ROOT.length + 1)}:${i + 1}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect(
|
||||||
|
hits,
|
||||||
|
`«${needle}» — инфраструктурное имя в файле пользовательской копии. ` +
|
||||||
|
`Посетителю оно не сообщает ничего: «на боевой базе» несёт весь смысл. ` +
|
||||||
|
`Строка может не рендериться сегодня и начать рендериться завтра — ` +
|
||||||
|
`так и вышло с CITY_COVERAGE_SOURCE в EstimateFlow.tsx.`,
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* ЧИСЛА ИЗ МАКЕТА СНЯТЫ (29.08.2026). Макет утверждал «по нашим данным,
|
* ЧИСЛА ИЗ МАКЕТА СНЯТЫ (29.08.2026). Макет утверждал «по нашим данным,
|
||||||
* средний разрыв между первой ценой в объявлении и ценой сделки — 4,1%, а у
|
* средний разрыв между первой ценой в объявлении и ценой сделки — 4,1%, а у
|
||||||
* провисевших дольше трёх месяцев доходит до 9%», «расхождение с кадастровой
|
* провисевших дольше трёх месяцев доходит до 9%», «расхождение с кадастровой
|
||||||
* до 40%» и плитки −4,3% / ×2,4 / 83%. Прод-замер (poincare, 29.08.2026)
|
* до 40%» и плитки −4,3% / ×2,4 / 83%. Прод-замер (29.08.2026)
|
||||||
* не подтвердил ни одно:
|
* не подтвердил ни одно:
|
||||||
*
|
*
|
||||||
* | макет | что показал замер |
|
* | макет | что показал замер |
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export interface MeasuredValue {
|
||||||
|
|
||||||
/** Общий источник трёх величин ниже — один и тот же прогон сверки. */
|
/** Общий источник трёх величин ниже — один и тот же прогон сверки. */
|
||||||
const BACKTEST_SOURCE =
|
const BACKTEST_SOURCE =
|
||||||
"Ручная сверка на проде (poincare, 29.08.2026): прогноз МЕРЫ против цены ДКП " +
|
"Ручная сверка на боевой базе (29.08.2026): прогноз МЕРЫ против цены ДКП " +
|
||||||
"Росреестра по Екатеринбургу, сделки II квартала 2026 года";
|
"Росреестра по Екатеринбургу, сделки II квартала 2026 года";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -204,7 +204,7 @@ export interface CityCoverageMeasure {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CITY_COVERAGE_SOURCE =
|
export const CITY_COVERAGE_SOURCE =
|
||||||
"Симуляция пробы покрытия на боевой базе (poincare, 30.08.2026): случайные адреса " +
|
"Симуляция пробы покрытия на боевой базе (30.08.2026): случайные адреса " +
|
||||||
"активных объявлений, для каждого — когорта самой ручки /coverage (радиус 1 км, то же " +
|
"активных объявлений, для каждого — когорта самой ручки /coverage (радиус 1 км, то же " +
|
||||||
"число комнат, площадь ±15 %, свежесть 14 дней, дедуп по источнику и адресу), " +
|
"число комнат, площадь ±15 %, свежесть 14 дней, дедуп по источнику и адресу), " +
|
||||||
"собственный адрес из когорты исключён";
|
"собственный адрес из когорты исключён";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue