5.5 KB169 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {Tracker} from 'isl-server/src/analytics/tracker';
9
10import {SuccessionTracker} from '../SuccessionTracker';
11import {Dag, DagCommitInfo} from '../dag/dag';
12import {COMMIT} from '../testUtils';
13
14describe('SuccessionTracker', () => {
15 const dag = new Dag().add(
16 [
17 COMMIT('111', 'Public commit', '0', {}),
18 COMMIT('aaa', 'Commit A', '1', {}),
19 COMMIT('bbb', 'Commit B', '1', {}),
20 ].map(DagCommitInfo.fromCommitInfo),
21 );
22 it('finds successions', () => {
23 const onSuccession = jest.fn();
24 const tracker = new SuccessionTracker();
25 const dispose = tracker.onSuccessions(onSuccession);
26
27 tracker.findNewSuccessionsFromCommits(dag, [
28 COMMIT('111', 'Public commit', '0', {}),
29 COMMIT('aaa', 'Commit A', '1', {}),
30 COMMIT('bbb', 'Commit B', '1', {}),
31 ]);
32 expect(onSuccession).not.toHaveBeenCalled();
33
34 tracker.findNewSuccessionsFromCommits(dag, [
35 COMMIT('111', 'Public commit', '0', {}),
36 COMMIT('aaa2', 'Commit A - updated', '1', {closestPredecessors: ['aaa']}),
37 COMMIT('bbb', 'Commit B', '1', {}),
38 ]);
39 expect(onSuccession).toHaveBeenCalledTimes(1);
40 expect(onSuccession).toHaveBeenCalledWith([['aaa', 'aaa2']]);
41
42 dispose();
43 });
44
45 it('only reports successions once', () => {
46 const onSuccession = jest.fn();
47 const tracker = new SuccessionTracker();
48 const dispose = tracker.onSuccessions(onSuccession);
49
50 tracker.findNewSuccessionsFromCommits(dag, [
51 COMMIT('111', 'Public commit', '0', {}),
52 COMMIT('aaa', 'Commit A', '1', {}),
53 COMMIT('bbb', 'Commit B', '1', {}),
54 ]);
55
56 tracker.findNewSuccessionsFromCommits(dag, [
57 COMMIT('111', 'Public commit', '0', {}),
58 COMMIT('aaa2', 'Commit A - updated', '1', {closestPredecessors: ['aaa']}),
59 COMMIT('bbb', 'Commit B', '1', {}),
60 ]);
61 tracker.findNewSuccessionsFromCommits(dag, [
62 COMMIT('111', 'Public commit', '0', {}),
63 COMMIT('aaa2', 'Commit A - updated', '1', {closestPredecessors: ['aaa']}),
64 COMMIT('bbb', 'Commit B', '1', {}),
65 ]);
66 expect(onSuccession).toHaveBeenCalledTimes(1);
67 expect(onSuccession).toHaveBeenCalledWith([['aaa', 'aaa2']]);
68
69 dispose();
70 });
71
72 it('skips new commits even if they have predecessors', () => {
73 const onSuccession = jest.fn();
74 const tracker = new SuccessionTracker();
75 const dispose = tracker.onSuccessions(onSuccession);
76
77 tracker.findNewSuccessionsFromCommits(dag, [
78 COMMIT('111', 'Public commit', '0', {}),
79 COMMIT('aaa', 'Commit A', '1', {closestPredecessors: ['a0']}),
80 COMMIT('bbb', 'Commit B', '1', {closestPredecessors: ['b0']}),
81 ]);
82 expect(onSuccession).not.toHaveBeenCalled();
83 dispose();
84 });
85
86 it('handles multiple predecessors (e.g. from fold)', () => {
87 const onSuccession = jest.fn();
88 const tracker = new SuccessionTracker();
89 const dispose = tracker.onSuccessions(onSuccession);
90
91 tracker.findNewSuccessionsFromCommits(dag, [
92 COMMIT('111', 'Public commit', '0', {}),
93 COMMIT('aaa', 'Commit A', '1', {}),
94 COMMIT('bbb', 'Commit B', 'aaa', {}),
95 COMMIT('ccc', 'Commit C', 'bbb', {}),
96 ]);
97 expect(onSuccession).not.toHaveBeenCalled();
98
99 tracker.findNewSuccessionsFromCommits(dag, [
100 COMMIT('111', 'Public commit', '0', {}),
101 COMMIT('aaa', 'Commit A', '1', {}),
102 COMMIT('bc', 'Fold B & C', '1', {closestPredecessors: ['bbb', 'ccc']}),
103 ]);
104 expect(onSuccession).toHaveBeenCalledTimes(1);
105 expect(onSuccession).toHaveBeenCalledWith([
106 ['bbb', 'bc'],
107 ['ccc', 'bc'],
108 ]);
109
110 dispose();
111 });
112
113 it('looks for "buggy" successsions that modify diff numbers', () => {
114 const onSuccession = jest.fn();
115 const tracker = new SuccessionTracker();
116 const dispose = tracker.onSuccessions(onSuccession);
117 const mockTrack = jest.fn();
118
119 window.globalIslClientTracker = {track: mockTrack} as unknown as Tracker<Record<string, never>>;
120
121 let dag = new Dag().add(
122 [
123 COMMIT('111', 'Public commit', '0', {}),
124 COMMIT('aaa', 'Commit A', '1', {}),
125 COMMIT('bbb', 'Commit B', '1', {}),
126 ].map(DagCommitInfo.fromCommitInfo),
127 );
128
129 tracker.findNewSuccessionsFromCommits(dag, [
130 COMMIT('111', 'Public commit', '0', {}),
131 COMMIT('aaa', 'Commit A', '1', {diffId: 'D111', description: 'old: D111'}),
132 COMMIT('bbb', 'Commit B', '1', {diffId: 'D222'}),
133 ]);
134 expect(onSuccession).not.toHaveBeenCalled();
135
136 dag = new Dag().add(
137 [
138 COMMIT('111', 'Public commit', '0', {}),
139 COMMIT('aaa', 'Commit A', '1', {diffId: 'D111', description: 'old: D111'}),
140 COMMIT('bbb', 'Commit B', '1', {diffId: 'D222'}),
141 ].map(DagCommitInfo.fromCommitInfo),
142 );
143
144 tracker.findNewSuccessionsFromCommits(dag, [
145 COMMIT('111', 'Public commit', '0', {}),
146 COMMIT('aaa2', 'Commit A - updated', '1', {
147 closestPredecessors: ['aaa'],
148 diffId: 'D333',
149 description: 'new: D333',
150 }),
151 COMMIT('bbb', 'Commit B', '1', {diffId: 'D222'}),
152 ]);
153 expect(onSuccession).toHaveBeenCalledTimes(0);
154
155 expect(mockTrack).toHaveBeenCalledWith('BuggySuccessionDetected', {
156 extras: {
157 oldHash: 'aaa',
158 newHash: 'aaa2',
159 old: 'Commit A\nold: D111',
160 new: 'Commit A - updated\nnew: D333',
161 },
162 });
163
164 dispose();
165
166 delete (window as {globalIslClientTracker?: unknown}).globalIslClientTracker;
167 });
168});
169