From 850d1925b6726f7e7f62d478ea0deb2c7d0dcd02 Mon Sep 17 00:00:00 2001 From: kenneth Date: Sat, 17 Jan 2026 01:09:39 +0000 Subject: [PATCH] Add query() tests with mocked API response Tests transformation logic including: - Feed item type assignment - Hourly/daily limits - Timestamp from context - Unit conversion (metric/imperial) - Priority assignment - Unique ID generation Co-authored-by: Ona --- .../src/data-source.test.ts | 137 +++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/packages/aris-data-source-weatherkit/src/data-source.test.ts b/packages/aris-data-source-weatherkit/src/data-source.test.ts index d6bbfc4..c63ebe1 100644 --- a/packages/aris-data-source-weatherkit/src/data-source.test.ts +++ b/packages/aris-data-source-weatherkit/src/data-source.test.ts @@ -1,10 +1,11 @@ import type { Context } from "@aris/core" -import { describe, expect, test } from "bun:test" +import { describe, expect, mock, test } from "bun:test" import fixture from "../fixtures/san-francisco.json" import { WeatherKitDataSource, Units } from "./data-source" import { WeatherFeedItemType } from "./feed-items" +import * as weatherkit from "./weatherkit" const mockCredentials = { privateKey: "mock", @@ -101,3 +102,137 @@ describe("unit conversion", () => { expect(Units.imperial).toBe("imperial") }) }) + +describe("query() with mocked API", () => { + const mockFetchWeather = mock(() => + Promise.resolve(fixture.response as weatherkit.WeatherKitResponse), + ) + + test("transforms API response into feed items", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ credentials: mockCredentials }) + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + + const items = await dataSource.query(context) + + expect(items.length).toBeGreaterThan(0) + expect(items.some((i) => i.type === WeatherFeedItemType.current)).toBe(true) + expect(items.some((i) => i.type === WeatherFeedItemType.hourly)).toBe(true) + expect(items.some((i) => i.type === WeatherFeedItemType.daily)).toBe(true) + }) + + test("applies hourly and daily limits", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ + credentials: mockCredentials, + hourlyLimit: 3, + dailyLimit: 2, + }) + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + + const items = await dataSource.query(context) + + const hourlyItems = items.filter((i) => i.type === WeatherFeedItemType.hourly) + const dailyItems = items.filter((i) => i.type === WeatherFeedItemType.daily) + + expect(hourlyItems.length).toBe(3) + expect(dailyItems.length).toBe(2) + }) + + test("sets timestamp from context.time", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ credentials: mockCredentials }) + const queryTime = new Date("2026-01-17T12:00:00Z") + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + context.time = queryTime + + const items = await dataSource.query(context) + + for (const item of items) { + expect(item.timestamp).toEqual(queryTime) + } + }) + + test("converts temperatures to imperial", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource, Units } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ credentials: mockCredentials }) + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + + const metricItems = await dataSource.query(context, { units: Units.metric }) + const imperialItems = await dataSource.query(context, { units: Units.imperial }) + + const metricCurrent = metricItems.find((i) => i.type === WeatherFeedItemType.current) + const imperialCurrent = imperialItems.find((i) => i.type === WeatherFeedItemType.current) + + expect(metricCurrent).toBeDefined() + expect(imperialCurrent).toBeDefined() + + // Imperial temp should be higher (F > C for typical weather temps) + const metricTemp = (metricCurrent!.data as { temperature: number }).temperature + const imperialTemp = (imperialCurrent!.data as { temperature: number }).temperature + + // Verify conversion: F = C * 9/5 + 32 + const expectedImperial = (metricTemp * 9) / 5 + 32 + expect(imperialTemp).toBeCloseTo(expectedImperial, 2) + }) + + test("assigns priority based on weather conditions", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ credentials: mockCredentials }) + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + + const items = await dataSource.query(context) + + for (const item of items) { + expect(item.priority).toBeGreaterThanOrEqual(0) + expect(item.priority).toBeLessThanOrEqual(1) + } + + const currentItem = items.find((i) => i.type === WeatherFeedItemType.current) + expect(currentItem).toBeDefined() + // Base priority for current is 0.5, may be adjusted for conditions + expect(currentItem!.priority).toBeGreaterThanOrEqual(0.5) + }) + + test("generates unique IDs for each item", async () => { + mock.module("./weatherkit", () => ({ + ...weatherkit, + fetchWeather: mockFetchWeather, + })) + + const { WeatherKitDataSource } = await import("./data-source") + const dataSource = new WeatherKitDataSource({ credentials: mockCredentials }) + const context = createMockContext({ lat: 37.7749, lng: -122.4194 }) + + const items = await dataSource.query(context) + const ids = items.map((i) => i.id) + const uniqueIds = new Set(ids) + + expect(uniqueIds.size).toBe(ids.length) + }) +})