Skip to content

Commit

Permalink
update offline timer (#2267)
Browse files Browse the repository at this point in the history
  • Loading branch information
Karlie-777 authored Feb 12, 2024
1 parent d88307e commit 150f931
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 40 deletions.
20 changes: 20 additions & 0 deletions channels/offline-channel-js/Tests/Unit/src/TestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class TestChannel extends BaseTelemetryPlugin implements IChannelControls
public identifier = BreezeChannelIdentifier;
public priority: number = 1001;
public endpoint: string = DEFAULT_BREEZE_ENDPOINT + DEFAULT_BREEZE_PATH;
public isIdle: boolean = true;

lastEventAdded: ITelemetryItem;
eventsAdded: ITelemetryItem[] = [];
Expand Down Expand Up @@ -40,6 +41,13 @@ export class TestChannel extends BaseTelemetryPlugin implements IChannelControls
this.flushCalled = true;
}

setIsIdle(val) {
this.isIdle = val;
}
isCompletelyIdle() {
return this.isIdle;
}

getOfflineSupport() {
return {
serialize: (evt) => {
Expand Down Expand Up @@ -70,5 +78,17 @@ export class TestChannel extends BaseTelemetryPlugin implements IChannelControls
}
}

export function mockTelemetryItem(): ITelemetryItem {
let evt = {
ver: "testVer" + Math.random(),
name:"testName",
time: "testTime",
iKey:"testKey",
baseData: {pro1: "prop1"},
baseType: "testType"
} as ITelemetryItem;
return evt;
}



18 changes: 3 additions & 15 deletions channels/offline-channel-js/Tests/Unit/src/channel.tests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AITestClass, Assert, PollingAssert } from "@microsoft/ai-test-framework";
import { DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, IConfig } from "@microsoft/applicationinsights-common";
import { AppInsightsCore, IConfiguration, ITelemetryItem, getGlobal, getGlobalInst } from "@microsoft/applicationinsights-core-js";
import { TestChannel } from "./TestHelper";
import { AppInsightsCore, IConfiguration, getGlobal, getGlobalInst } from "@microsoft/applicationinsights-core-js";
import { TestChannel, mockTelemetryItem } from "./TestHelper";
import { OfflineChannel } from "../../../src/OfflineChannel"
import { IOfflineChannelConfiguration, eStorageProviders } from "../../../src/applicationinsights-offlinechannel-js";

Expand Down Expand Up @@ -123,7 +123,7 @@ export class ChannelTests extends AITestClass {

let storageObj = JSON.parse(storageStr);
let evts = storageObj.evts;
Assert.deepEqual(Object.keys(evts).length, 1, "storgae should have one event");
Assert.deepEqual(Object.keys(evts).length, 1, "storage should have one event");

this.clock.tick(10);

Expand Down Expand Up @@ -292,15 +292,3 @@ export class ChannelTests extends AITestClass {
});
}
}

function mockTelemetryItem(): ITelemetryItem {
let evt = {
ver: "testVer" + Math.random(),
name:"testName",
time: "testTime",
iKey:"testKey",
baseData: {pro1: "prop1"},
baseType: "testType"
} as ITelemetryItem;
return evt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { OfflineDbProviderTests } from "./dbprovider.tests"
import { OfflineInMemoryBatchTests } from "./inmemorybatch.tests";
import { OfflineBatchHandlerTests } from "./offlinebatchhandler.tests";
import { ChannelTests } from "./channel.tests";
import { Offlinetimer } from "./offlinetimer.tests";

export function runTests() {
new OfflineIndexedDBTests().registerTests();
Expand All @@ -12,4 +13,5 @@ export function runTests() {
new OfflineInMemoryBatchTests().registerTests();
new OfflineBatchHandlerTests().registerTests();
new ChannelTests().registerTests();
new Offlinetimer().registerTests();
}
227 changes: 227 additions & 0 deletions channels/offline-channel-js/Tests/Unit/src/offlinetimer.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { AITestClass, Assert } from "@microsoft/ai-test-framework";
import { DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, EventPersistence, IConfig } from "@microsoft/applicationinsights-common";
import { AppInsightsCore, IConfiguration, IPayloadData, OnCompleteCallback } from "@microsoft/applicationinsights-core-js";
import { TestChannel, mockTelemetryItem } from "./TestHelper";
import { OfflineChannel } from "../../../src/OfflineChannel";
import { IOfflineChannelConfiguration, eStorageProviders } from "../../../src/Interfaces/IOfflineProvider";
import { IPostTransmissionTelemetryItem } from "../../../src/applicationinsights-offlinechannel-js";

export class Offlinetimer extends AITestClass {
private core: AppInsightsCore;
private coreConfig: IConfig & IConfiguration;

public testInitialize() {
super.testInitialize();
AITestClass.orgLocalStorage.clear();

this.coreConfig = {
instrumentationKey: "testIkey",
endpointUrl: DEFAULT_BREEZE_ENDPOINT + DEFAULT_BREEZE_PATH
};
this.core = new AppInsightsCore();

}

public testCleanup() {
super.testCleanup();
AITestClass.orgLocalStorage.clear();
this.onDone(() => {
this.core.unload();
});
this.core = null as any;
this.coreConfig = null as any;

}

public registerTests() {

this.testCase({
name: "InMemo Timer: Handle in memory timer",
useFakeTimers: true,
test: () => {
this.coreConfig.extensionConfig = {["OfflineChannel"]: {providers:[eStorageProviders.LocalStorage], inMemoMaxTime: 2000, minPersistenceLevel: EventPersistence.Critical, eventsLimitInMem: 2 } as IOfflineChannelConfiguration};
let channel = new OfflineChannel();
let onlineChannel = new TestChannel();
this.core.initialize(this.coreConfig,[channel, onlineChannel]);
let offlineListener = channel.getOfflineListener() as any;

// online, processTelemetry is not called
offlineListener.setOnlineState(1);
let inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer, "in memo timer should be null");

// offline, processTelemetry is not called
offlineListener.setOnlineState(2);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer, "in memo timer should be null test1");

// online, processTelemetry is called
offlineListener.setOnlineState(1);
let evt = mockTelemetryItem();
channel.processTelemetry(evt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer, "in memo timer should be null test2");

// offline, processTelemetry is called with event that should not be sent
offlineListener.setOnlineState(2);
evt = mockTelemetryItem();
channel.processTelemetry(evt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer, "in memo timer should be null test3");

// offline, processTelemetry is called with event that should be sent
offlineListener.setOnlineState(2);
let validEvt = mockTelemetryItem() as IPostTransmissionTelemetryItem;
validEvt.persistence = 2;
channel.processTelemetry(validEvt);
channel.processTelemetry(validEvt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(inMemoTimer, "in memo timer should be created");
Assert.ok(inMemoTimer.enabled, "in memo timer should be enabled");
let inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 2, "should have two events");

// offline, flush all events in memory, and processTelemetry is not called again
this.clock.tick(2000);
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 0, "should have no event left");
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer.enabled, "in memo timer enabled should be false with no events in memory");

this.clock.tick(2000);
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 0, "should have no event left");
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer.enabled, "in memo timer enabled should be false with no events in memory and no processTelemtry is called");

// offline, flush all events and with one event left in memory, and processTelemetry is not called again
channel.processTelemetry(validEvt);
channel.processTelemetry(validEvt);
channel.processTelemetry(validEvt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(inMemoTimer.enabled, "in memo timer should be enabled");
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 1, "should have one event left after flush");

this.clock.tick(2000);
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 0, "should have no event left");
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer.enabled, "in memo timer should be canceld with no events in memory test1");

// offline with one event saved in memory, and then online with processTelemetry called
channel.processTelemetry(validEvt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(inMemoTimer.enabled, "in memo timer should be enabled");
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 1, "should have one event left after flush");

offlineListener.setOnlineState(1);
this.clock.tick(2000);
inMemoBatch = channel["_getDbgPlgTargets"]()[1];
Assert.equal(inMemoBatch && inMemoBatch.count(), 0, "should have no event left");
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer.enabled, "in memo timer should be canceld with no events in memory test2");

channel.processTelemetry(validEvt);
inMemoTimer = channel["_getDbgPlgTargets"]()[3];
Assert.ok(!inMemoTimer.enabled, "in memo timer should be canceld when back online");


channel.teardown();

}
});

this.testCase({
name: "SendNextBatch Timer: Handle sendNextBatch timer",
useFakeTimers: true,
test: () => {
let called = 0;
let sendPost = (payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) => {
// first call, return complete, data null;
called ++;
console.log("test")
oncomplete(200, {});
return;

}

let xhrOverride = {sendPOST: sendPost}
this.coreConfig.extensionConfig = {["OfflineChannel"]: {providers:[eStorageProviders.LocalStorage], inMemoMaxTime: 2000, eventsLimitInMem: 2, maxSentBatchInterval: 10000,
senderCfg: {httpXHROverride: xhrOverride, alwaysUseXhrOverride: true}
} as IOfflineChannelConfiguration};
let channel = new OfflineChannel();
let onlineChannel = new TestChannel();
this.core.initialize(this.coreConfig,[channel, onlineChannel]);
let offlineListener = channel.getOfflineListener() as any;

// online, processTelemetry is not called
offlineListener.setOnlineState(1);
let sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(sendBatchTimer, "sendBatch timer should be created after init");
Assert.ok(sendBatchTimer.enabled, "sendBatch timer should be enabled");

// online, processTelemetry is not called, no previous stored events returned
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(!sendBatchTimer.enabled, "sendBatch timer should not start again with no events in storage");

// online, processTelemetry called, no previous stored events returned
this.clock.tick(10000);
let evt = mockTelemetryItem() as IPostTransmissionTelemetryItem;
evt.persistence = 2;
channel.processTelemetry(evt);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(!sendBatchTimer.enabled, "sendBatch timer should not start again with no events in storage test1");
Assert.equal(called, 0 , "no data is sent");

// offline, processTelemetry is not called
offlineListener.setOnlineState(2);
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(!sendBatchTimer.enabled, "sendBatch timer should not start again with no events in storage test2");
Assert.equal(called, 0 , "no data is sent");

// offline, processTelemetry is called, in Memory flush called
channel.processTelemetry(evt);
channel.processTelemetry(evt);
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(!sendBatchTimer.enabled, "sendBatch timer should not start again with no events in storage test3");
Assert.equal(called, 0 , "no data is sent");

// online, with online sender is not idle
offlineListener.setOnlineState(1);
onlineChannel.setIsIdle(false);
channel.processTelemetry(evt);
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(sendBatchTimer.enabled, "sendBatch timer be refreshed when it is online and not idle");
Assert.equal(called, 0 , "no data is sent");

// online, with online sender is idle
offlineListener.setOnlineState(1);
onlineChannel.setIsIdle(true);
channel.processTelemetry(evt);
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(sendBatchTimer.enabled, "sendBatch timer be refreshed when it is online and idle");
Assert.equal(called, 1 , "data is sent");

// online, with processTelemtry called again
offlineListener.setOnlineState(1);
channel.processTelemetry(evt);
this.clock.tick(10000);
sendBatchTimer = channel["_getDbgPlgTargets"]()[4];
Assert.ok(!sendBatchTimer.enabled, "sendBatch timer not be refreshed when it is online and no stored events");
Assert.equal(called, 1 , "no data is sent again");



channel.teardown();

}
});
}
}
4 changes: 2 additions & 2 deletions channels/offline-channel-js/src/InMemoryBatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export class InMemoryBatch implements IInMemoryBatch {
return new InMemoryBatch(logger, endpoint, theEvts, evtsLimitInMem);
};

_self.createNew = (newEndpoint: string, evts?: IPostTransmissionTelemetryItem[] | ITelemetryItem[], evtsLimitInMem?: number) => {
return new InMemoryBatch(logger, newEndpoint, evts,evtsLimitInMem);
_self.createNew = (newEndpoint: string, evts?: IPostTransmissionTelemetryItem[] | ITelemetryItem[], newEvtsLimitInMem?: number) => {
return new InMemoryBatch(logger, newEndpoint, evts, newEvtsLimitInMem);
}

});
Expand Down
Loading

0 comments on commit 150f931

Please sign in to comment.