feat: weather conditions check from stroy-bot-main
Ports the weather check added to stroy-bot-main on 2026-10-16: daily station weather, report positions with weather per shift and the AI assessment of working conditions. Opens as ?page=weather&start=&end= instead of the #weathercheck hash, and hub dates survive the way back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { fetchWeatherCheck, requestWeatherAssessment, WEATHER_PATHS } from './weatherRepository';
|
||||
import { IWeatherPosition, Nullable } from './weatherTypes';
|
||||
import { createFakeHttpClient } from '../../shared/testing/fakeHttpClient';
|
||||
|
||||
const API = 'https://api.tnsu.kz';
|
||||
const PERIOD = { start: '2026-10-01', end: '2026-10-07' };
|
||||
|
||||
const position = (profession: string, assessment: Nullable<string> = null): IWeatherPosition => ({
|
||||
project: 'ТЕСТ',
|
||||
profession,
|
||||
date: '2026-10-01',
|
||||
work_type: 'Бетонирование',
|
||||
hours: 8,
|
||||
hours_type: null,
|
||||
headcount: 2,
|
||||
shift: '1',
|
||||
shift_start: '08:00',
|
||||
shift_end: '20:00',
|
||||
weather: null,
|
||||
assessment,
|
||||
comment: null,
|
||||
downtime_act_recommended: null,
|
||||
downtime_act_reason: null
|
||||
});
|
||||
|
||||
describe('адреса сверки погоды (1:1 со stroy-bot-main)', () => {
|
||||
it('период — Unix-секунды UTC: 00:00:00 первого дня и 23:59:59 последнего', () => {
|
||||
expect(WEATHER_PATHS.check(API, 51, PERIOD)).toEqual(
|
||||
'https://api.tnsu.kz/Report/StroyBot/WeatherCheck?start=1790812800&end=1791417599&projectId=51'
|
||||
);
|
||||
});
|
||||
|
||||
it('оценка ИИ', () => {
|
||||
expect(WEATHER_PATHS.assess(API)).toEqual('https://api.tnsu.kz/Report/StroyBot/WeatherCheck/Assess');
|
||||
});
|
||||
});
|
||||
|
||||
describe('запросы', () => {
|
||||
it('сверка — GET, ответ отдаётся как есть', async () => {
|
||||
const body = { project: 'ТЕСТ', days: [], positions: [], notes: [] };
|
||||
const client = createFakeHttpClient([{ match: 'WeatherCheck?', response: { body } }]);
|
||||
expect(await fetchWeatherCheck(client, API, 51, PERIOD)).toEqual(body);
|
||||
expect(client.requests[0].request.method).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ошибка API пробрасывается', async () => {
|
||||
const client = createFakeHttpClient([{ match: 'WeatherCheck?', response: { status: 500, body: 'boom' } }]);
|
||||
await expect(fetchWeatherCheck(client, API, 51, PERIOD)).rejects.toThrow('500');
|
||||
});
|
||||
|
||||
it('оценка — POST со всеми позициями в теле { positions }', async () => {
|
||||
const result = { assessed: 1, skipped: 0, positions: [position('Бетонщик', 'В пределах нормы')] };
|
||||
const client = createFakeHttpClient([{ match: '/Assess', response: { body: result } }]);
|
||||
|
||||
expect(await requestWeatherAssessment(client, API, [position('Бетонщик')])).toEqual(result);
|
||||
|
||||
const sent = client.requests[0].request;
|
||||
expect(sent.method).toEqual('POST');
|
||||
expect(sent.headers).toEqual({ accept: '*/*', 'content-type': 'application/json' });
|
||||
expect(JSON.parse(String(sent.body))).toEqual({ positions: [position('Бетонщик')] });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user