Skip to content

Commit

Permalink
Fixed unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
timoschlueter committed Mar 26, 2024
1 parent b8be1d5 commit 5fa3111
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 66 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,11 @@ async function uploadToNightScout(measurementData: GraphData): Promise<void>
if (formattedMeasurements.length > 0)
{
logger.info("Trying to upload " + formattedMeasurements.length + " glucose measurement items to Nightscout");
try
try
{
await nightscoutClient.uploadEntries(formattedMeasurements);
logger.info("Upload of " + formattedMeasurements.length + " measurements to Nightscout succeeded");
} catch (error)
} catch (error)
{
logger.error("Upload to NightScout failed ", error);
}
Expand Down
110 changes: 61 additions & 49 deletions src/nightscout/apiv1.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,65 @@
import { Entry, NightscoutAPI, NightscoutConfig } from './interface';
import { OutgoingHttpHeaders } from 'http';
import axios from 'axios';
import {Entry, NightscoutAPI, NightscoutConfig} from "./interface";
import {OutgoingHttpHeaders} from "http";
import axios from "axios";

interface NightscoutHttpHeaders extends OutgoingHttpHeaders {
'api-secret': string | undefined;
interface NightscoutHttpHeaders extends OutgoingHttpHeaders
{
"api-secret": string | undefined;
}

export class Client implements NightscoutAPI {
readonly baseUrl: string;
readonly headers: NightscoutHttpHeaders;
readonly device: string;

constructor(config: NightscoutConfig) {
this.baseUrl = config.nightscoutBaseUrl;
this.headers = {
'api-secret': config.nightscoutApiToken,
'User-Agent': 'FreeStyle LibreLink Up NightScout Uploader',
'Content-Type': 'application/json',
};
this.device = config.nightscoutDevice;
}

async lastEntry(): Promise<Entry | null> {
const url = new URL('/api/v1/entries?count=1', this.baseUrl).toString();
const resp = await axios.get(url, { headers: this.headers });
if (resp.status !== 200) {
throw Error(`failed to get last entry: ${resp.statusText}`);
}
if (!resp.data || resp.data.length === 0) {
return null;
}
return resp.data.pop();
}

async uploadEntries(entries: Entry[]): Promise<void> {
const url = new URL('/api/v1/entries', this.baseUrl).toString();
const entriesV1 = entries.map((e) => ({
type: 'sgv',
sgv: e.sgv,
direction: e.direction?.toString(),
device: this.device,
date: e.date.getTime(),
dateString: e.date.toISOString(),
}));
const resp = await axios.post(url, entriesV1, { headers: this.headers });
if (resp.status !== 200) {
throw Error(`failed to post new entries: ${resp.statusText}`);
}

return;
}
export class Client implements NightscoutAPI
{
readonly baseUrl: string;
readonly headers: NightscoutHttpHeaders;
readonly device: string;

constructor(config: NightscoutConfig)
{
this.baseUrl = config.nightscoutBaseUrl;
this.headers = {
"api-secret": config.nightscoutApiToken,
"User-Agent": "FreeStyle LibreLink Up NightScout Uploader",
"Content-Type": "application/json",
};
this.device = config.nightscoutDevice;
}

async lastEntry(): Promise<Entry | null>
{
const url = new URL("/api/v1/entries?count=1", this.baseUrl).toString();
const resp = await axios.get(url, {headers: this.headers});

if (resp.status !== 200)
{
throw Error(`failed to get last entry: ${resp.statusText}`);
}

if (!resp.data || resp.data.length === 0)
{
return null;
}
return resp.data.pop();
}

async uploadEntries(entries: Entry[]): Promise<void>
{
const url = new URL("/api/v1/entries", this.baseUrl).toString();
const entriesV1 = entries.map((e) => ({
type: "sgv",
sgv: e.sgv,
direction: e.direction?.toString(),
device: this.device,
date: e.date.getTime(),
dateString: e.date.toISOString(),
}));

const resp = await axios.post(url, entriesV1, {headers: this.headers});

if (resp.status !== 200)
{
throw Error(`failed to post new entries: ${resp.statusText}`);
}

return;
}
}
1 change: 1 addition & 0 deletions tests/setup.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
process.env.SINGLE_SHOT="true";
process.env.NIGHTSCOUT_URL="localhost:1337";
process.env.NIGHTSCOUT_DISABLE_HTTPS="true";
process.env.NIGHTSCOUT_API_TOKEN="abcdefg";
31 changes: 18 additions & 13 deletions tests/unit-tests/librelink/librelink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@ import {
} from "../../../src";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";

const mock = new MockAdapter(axios);
import { default as loginSuccessResponse } from "../../data/login.json";
import { default as loginFailedResponse } from "../../data/login-failed.json";
import { default as connectionsResponse } from "../../data/connections.json";
import { default as entriesResponse } from "../../data/entries.json";
import { default as graphResponse } from "../../data/graph.json";
import {default as loginSuccessResponse} from "../../data/login.json";
import {default as loginFailedResponse} from "../../data/login-failed.json";
import {default as connectionsResponse} from "../../data/connections.json";
import {default as entriesResponse} from "../../data/entries.json";
import {default as graphResponse} from "../../data/graph.json";
import {AuthTicket} from "../../../src/interfaces/librelink/common";
import {GraphData} from "../../../src/interfaces/librelink/graph-response";
import {Entry} from "../../../src/nightscout/interface";

mock.onPost("https://api-eu.libreview.io/llu/auth/login").reply(200, loginSuccessResponse);
mock.onGet("https://api-eu.libreview.io/llu/connections").reply(200, connectionsResponse);
mock.onGet("https://api-eu.libreview.io/llu/connections/7ad66b40-ba9b-401e-9845-4f49f998cf16/graph").reply(200, graphResponse);
mock.onGet("http://localhost:1337/api/v1/entries?count=1").reply(200, []);

describe("LibreLink Up", () => {
describe("LibreLink Up", () =>
{
const env = process.env

beforeEach(() => {
beforeEach(() =>
{
jest.resetModules()
process.env = { ...env }
process.env = {...env}
})

afterEach(() => {
afterEach(() =>
{
process.env = env
})

Expand Down Expand Up @@ -89,11 +94,11 @@ describe("LibreLink Up", () => {
const formattedMeasurements: Entry[] = await createFormattedMeasurements(glucoseMeasurements);
expect(formattedMeasurements.length).toBe(142);

expect(formattedMeasurements[0].date).toBe(1672418860000);
expect(formattedMeasurements[0].date.getTime()).toBe(1672418860000);
expect(formattedMeasurements[0].direction).toBe("Flat");
expect(formattedMeasurements[0].sgv).toBe(115);

expect(formattedMeasurements[1].date).toBe(1672375840000);
expect(formattedMeasurements[1].date.getTime()).toBe(1672375840000);
expect(formattedMeasurements[1]).not.toHaveProperty("direction");
expect(formattedMeasurements[1].sgv).toBe(173);
});
Expand All @@ -110,11 +115,11 @@ describe("LibreLink Up", () => {
const formattedMeasurements: Entry[] = await createFormattedMeasurements(glucoseMeasurements);
expect(formattedMeasurements.length).toBe(112);

expect(formattedMeasurements[0].date).toBe(1672418860000);
expect(formattedMeasurements[0].date.getTime()).toBe(1672418860000);
expect(formattedMeasurements[0].direction).toBe("Flat");
expect(formattedMeasurements[0].sgv).toBe(115);

expect(formattedMeasurements[1].date).toBe(1672384839000);
expect(formattedMeasurements[1].date.getTime()).toBe(1672384839000);
expect(formattedMeasurements[1]).not.toHaveProperty("direction");
expect(formattedMeasurements[1].sgv).toBe(177);
});
Expand Down

0 comments on commit 5fa3111

Please sign in to comment.