mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
unit test latest metrics
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
describe("AchievementHub", () => {
|
||||
describe("earned$", () => {});
|
||||
|
||||
describe("metrics$", () => {});
|
||||
|
||||
describe("all$", () => {});
|
||||
|
||||
describe("named$", () => {});
|
||||
});
|
||||
@@ -1,7 +1,75 @@
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { AchievementProgressEvent } from "../types";
|
||||
import { AchievementEarnedEvent, AchievementProgressEvent } from "../types";
|
||||
|
||||
import { CredentialGeneratedProgress, ItemCreatedProgress } from "./example-validators";
|
||||
import {
|
||||
CredentialGeneratedProgress,
|
||||
ItemCreatedAchievement,
|
||||
ItemCreatedProgress,
|
||||
ThreeItemsCreatedAchievement,
|
||||
} from "./example-validators";
|
||||
|
||||
const ItemCreatedEarnedEvent: AchievementEarnedEvent = {
|
||||
"@timestamp": Date.now(),
|
||||
event: {
|
||||
kind: "metric",
|
||||
category: "session",
|
||||
},
|
||||
achievement: { type: "earned", name: ItemCreatedAchievement },
|
||||
service: {
|
||||
name: "extension",
|
||||
type: "client",
|
||||
node: {
|
||||
name: "an-installation-identifier-for-this-client-instance",
|
||||
},
|
||||
environment: "local",
|
||||
version: "2025.3.1-innovation-sprint",
|
||||
},
|
||||
user: {
|
||||
id: "some-guid" as UserId,
|
||||
},
|
||||
};
|
||||
|
||||
const NextItemCreatedEarnedEvent: AchievementEarnedEvent = {
|
||||
"@timestamp": Date.now() + 100,
|
||||
event: {
|
||||
kind: "metric",
|
||||
category: "session",
|
||||
},
|
||||
achievement: { type: "earned", name: ItemCreatedAchievement },
|
||||
service: {
|
||||
name: "extension",
|
||||
type: "client",
|
||||
node: {
|
||||
name: "an-installation-identifier-for-this-client-instance",
|
||||
},
|
||||
environment: "local",
|
||||
version: "2025.3.1-innovation-sprint",
|
||||
},
|
||||
user: {
|
||||
id: "some-guid" as UserId,
|
||||
},
|
||||
};
|
||||
|
||||
const ThreeItemsCreatedEarnedEvent: AchievementEarnedEvent = {
|
||||
"@timestamp": Date.now(),
|
||||
event: {
|
||||
kind: "metric",
|
||||
category: "session",
|
||||
},
|
||||
achievement: { type: "earned", name: ThreeItemsCreatedAchievement },
|
||||
service: {
|
||||
name: "extension",
|
||||
type: "client",
|
||||
node: {
|
||||
name: "an-installation-identifier-for-this-client-instance",
|
||||
},
|
||||
environment: "local",
|
||||
version: "2025.3.1-innovation-sprint",
|
||||
},
|
||||
user: {
|
||||
id: "some-guid" as UserId,
|
||||
},
|
||||
};
|
||||
|
||||
const ItemCreatedProgressEvent: AchievementProgressEvent = {
|
||||
"@timestamp": Date.now(),
|
||||
@@ -67,7 +135,10 @@ const CredentialGeneratedProgressEvent: AchievementProgressEvent = {
|
||||
};
|
||||
|
||||
export {
|
||||
ItemCreatedProgressEvent,
|
||||
NextItemCreatedProgressEvent as ItemCreatedProgress2Event,
|
||||
CredentialGeneratedProgressEvent,
|
||||
ItemCreatedEarnedEvent,
|
||||
ItemCreatedProgressEvent,
|
||||
NextItemCreatedEarnedEvent,
|
||||
NextItemCreatedProgressEvent,
|
||||
ThreeItemsCreatedEarnedEvent,
|
||||
};
|
||||
|
||||
@@ -3,13 +3,21 @@ import { BehaviorSubject, Subject } from "rxjs";
|
||||
import {
|
||||
CredentialGeneratedProgressEvent,
|
||||
ItemCreatedProgressEvent,
|
||||
ItemCreatedProgress2Event,
|
||||
NextItemCreatedProgressEvent,
|
||||
ItemCreatedEarnedEvent,
|
||||
NextItemCreatedEarnedEvent,
|
||||
ThreeItemsCreatedEarnedEvent,
|
||||
} from "./examples/achievement-events";
|
||||
import { CredentialGeneratedProgress, ItemCreatedProgress } from "./examples/example-validators";
|
||||
import { latestProgressMetrics } from "./latest-metrics";
|
||||
import { AchievementProgressEvent, MetricId } from "./types";
|
||||
import {
|
||||
CredentialGeneratedProgress,
|
||||
ItemCreatedAchievement,
|
||||
ItemCreatedProgress,
|
||||
ThreeItemsCreatedAchievement,
|
||||
} from "./examples/example-validators";
|
||||
import { latestEarnedMetrics, latestProgressMetrics } from "./latest-metrics";
|
||||
import { AchievementEarnedEvent, AchievementId, AchievementProgressEvent, MetricId } from "./types";
|
||||
|
||||
describe("latestMetrics", () => {
|
||||
describe("latestProgressMetrics", () => {
|
||||
it("creates a map containing a metric", () => {
|
||||
const subject = new Subject<AchievementProgressEvent>();
|
||||
const result = new BehaviorSubject(new Map<MetricId, AchievementProgressEvent>());
|
||||
@@ -38,9 +46,9 @@ describe("latestMetrics", () => {
|
||||
|
||||
subject.pipe(latestProgressMetrics()).subscribe(result);
|
||||
subject.next(ItemCreatedProgressEvent);
|
||||
subject.next(ItemCreatedProgress2Event);
|
||||
subject.next(NextItemCreatedProgressEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedProgress)).toEqual(ItemCreatedProgress2Event);
|
||||
expect(result.value.get(ItemCreatedProgress)).toEqual(NextItemCreatedProgressEvent);
|
||||
});
|
||||
|
||||
it("omits old events", () => {
|
||||
@@ -48,9 +56,55 @@ describe("latestMetrics", () => {
|
||||
const result = new BehaviorSubject(new Map<MetricId, AchievementProgressEvent>());
|
||||
|
||||
subject.pipe(latestProgressMetrics()).subscribe(result);
|
||||
subject.next(ItemCreatedProgress2Event);
|
||||
subject.next(NextItemCreatedProgressEvent);
|
||||
subject.next(ItemCreatedProgressEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedProgress)).toEqual(ItemCreatedProgress2Event);
|
||||
expect(result.value.get(ItemCreatedProgress)).toEqual(NextItemCreatedProgressEvent);
|
||||
});
|
||||
});
|
||||
|
||||
describe("latestEarnedMetrics", () => {
|
||||
it("creates a map containing a metric", () => {
|
||||
const subject = new Subject<AchievementEarnedEvent>();
|
||||
const result = new BehaviorSubject(new Map<AchievementId, AchievementEarnedEvent>());
|
||||
|
||||
subject.pipe(latestEarnedMetrics()).subscribe(result);
|
||||
subject.next(ItemCreatedEarnedEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedAchievement)).toEqual(ItemCreatedEarnedEvent);
|
||||
});
|
||||
|
||||
it("creates a map containing multiple metrics", () => {
|
||||
const subject = new Subject<AchievementEarnedEvent>();
|
||||
const result = new BehaviorSubject(new Map<AchievementId, AchievementEarnedEvent>());
|
||||
|
||||
subject.pipe(latestEarnedMetrics()).subscribe(result);
|
||||
subject.next(ItemCreatedEarnedEvent);
|
||||
subject.next(ThreeItemsCreatedEarnedEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedAchievement)).toEqual(ItemCreatedEarnedEvent);
|
||||
expect(result.value.get(ThreeItemsCreatedAchievement)).toEqual(ThreeItemsCreatedEarnedEvent);
|
||||
});
|
||||
|
||||
it("creates a map containing updated metrics", () => {
|
||||
const subject = new Subject<AchievementEarnedEvent>();
|
||||
const result = new BehaviorSubject(new Map<AchievementId, AchievementEarnedEvent>());
|
||||
|
||||
subject.pipe(latestEarnedMetrics()).subscribe(result);
|
||||
subject.next(ItemCreatedEarnedEvent);
|
||||
subject.next(NextItemCreatedEarnedEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedAchievement)).toEqual(NextItemCreatedEarnedEvent);
|
||||
});
|
||||
|
||||
it("omits old events", () => {
|
||||
const subject = new Subject<AchievementEarnedEvent>();
|
||||
const result = new BehaviorSubject(new Map<AchievementId, AchievementEarnedEvent>());
|
||||
|
||||
subject.pipe(latestEarnedMetrics()).subscribe(result);
|
||||
subject.next(NextItemCreatedEarnedEvent);
|
||||
subject.next(ItemCreatedEarnedEvent);
|
||||
|
||||
expect(result.value.get(ItemCreatedAchievement)).toEqual(NextItemCreatedEarnedEvent);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,7 @@
|
||||
import { OperatorFunction, map, filter, pipe, scan } from "rxjs";
|
||||
import { OperatorFunction, pipe, scan } from "rxjs";
|
||||
|
||||
import { MetricId, AchievementProgressEvent, AchievementId, AchievementEarnedEvent } from "./types";
|
||||
|
||||
function latestProgressEvents(): OperatorFunction<
|
||||
AchievementProgressEvent,
|
||||
AchievementProgressEvent
|
||||
> {
|
||||
type Accumulator = {
|
||||
latest: Map<MetricId, AchievementProgressEvent>;
|
||||
captured?: AchievementProgressEvent;
|
||||
};
|
||||
const acc: Accumulator = { latest: new Map() };
|
||||
|
||||
return pipe(
|
||||
scan((acc, captured) => {
|
||||
const { latest } = acc;
|
||||
const current = latest.get(captured.achievement.name);
|
||||
|
||||
// omit stale events
|
||||
if (current && current["@timestamp"] > captured["@timestamp"]) {
|
||||
return { latest };
|
||||
}
|
||||
|
||||
latest.set(captured.achievement.name, captured);
|
||||
return { latest, captured };
|
||||
}, acc),
|
||||
// omit updates caused by stale events
|
||||
filter(({ captured }) => !!captured),
|
||||
map(({ captured }) => captured!),
|
||||
);
|
||||
}
|
||||
|
||||
function latestProgressMetrics(): OperatorFunction<
|
||||
AchievementProgressEvent,
|
||||
Map<MetricId, AchievementProgressEvent>
|
||||
@@ -56,11 +27,18 @@ function latestEarnedMetrics(): OperatorFunction<
|
||||
Map<AchievementId, AchievementEarnedEvent>
|
||||
> {
|
||||
return pipe(
|
||||
scan((earned, captured) => {
|
||||
earned.set(captured.achievement.name, captured);
|
||||
return earned;
|
||||
scan((metrics, captured) => {
|
||||
const metric = metrics.get(captured.achievement.name);
|
||||
|
||||
// omit stale metrics
|
||||
if (metric && metric["@timestamp"] > captured["@timestamp"]) {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
metrics.set(captured.achievement.name, captured);
|
||||
return metrics;
|
||||
}, new Map<AchievementId, AchievementEarnedEvent>()),
|
||||
);
|
||||
}
|
||||
|
||||
export { latestProgressMetrics, latestProgressEvents, latestEarnedMetrics };
|
||||
export { latestProgressMetrics, latestEarnedMetrics };
|
||||
|
||||
Reference in New Issue
Block a user