|
|
@ -1,15 +1,50 @@ |
|
|
|
import { renderHook } from '@testing-library/react-hooks'; |
|
|
|
import { renderHook } from '@testing-library/react-hooks'; |
|
|
|
|
|
|
|
|
|
|
|
import { RuleIdentifier } from 'app/types/unified-alerting'; |
|
|
|
import { AlertingRule, RecordingRule, RuleIdentifier } from 'app/types/unified-alerting'; |
|
|
|
import { |
|
|
|
import { |
|
|
|
GrafanaAlertStateDecision, |
|
|
|
GrafanaAlertStateDecision, |
|
|
|
GrafanaRuleDefinition, |
|
|
|
GrafanaRuleDefinition, |
|
|
|
|
|
|
|
PromAlertingRuleState, |
|
|
|
|
|
|
|
PromRuleType, |
|
|
|
RulerAlertingRuleDTO, |
|
|
|
RulerAlertingRuleDTO, |
|
|
|
RulerGrafanaRuleDTO, |
|
|
|
RulerGrafanaRuleDTO, |
|
|
|
RulerRecordingRuleDTO, |
|
|
|
RulerRecordingRuleDTO, |
|
|
|
} from 'app/types/unified-alerting-dto'; |
|
|
|
} from 'app/types/unified-alerting-dto'; |
|
|
|
|
|
|
|
|
|
|
|
import { hashRulerRule, parse, stringifyIdentifier, getRuleIdFromPathname } from './rule-id'; |
|
|
|
import { hashRulerRule, parse, stringifyIdentifier, getRuleIdFromPathname, hashRule, equal } from './rule-id'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const alertingRule = { |
|
|
|
|
|
|
|
prom: { |
|
|
|
|
|
|
|
name: 'cpu-over-90', |
|
|
|
|
|
|
|
query: 'cpu_usage_seconds_total{job="integrations/node_exporter"} > 90', |
|
|
|
|
|
|
|
labels: { type: 'cpu' }, |
|
|
|
|
|
|
|
annotations: { description: 'CPU usage too high' }, |
|
|
|
|
|
|
|
state: PromAlertingRuleState.Firing, |
|
|
|
|
|
|
|
type: PromRuleType.Alerting, |
|
|
|
|
|
|
|
health: 'ok', |
|
|
|
|
|
|
|
} satisfies AlertingRule, |
|
|
|
|
|
|
|
ruler: { |
|
|
|
|
|
|
|
alert: 'cpu-over-90', |
|
|
|
|
|
|
|
expr: 'cpu_usage_seconds_total{job="integrations/node_exporter"} > 90', |
|
|
|
|
|
|
|
labels: { type: 'cpu' }, |
|
|
|
|
|
|
|
annotations: { description: 'CPU usage too high' }, |
|
|
|
|
|
|
|
} satisfies RulerAlertingRuleDTO, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const recordingRule = { |
|
|
|
|
|
|
|
prom: { |
|
|
|
|
|
|
|
name: 'instance:node_num_cpu:sum', |
|
|
|
|
|
|
|
type: PromRuleType.Recording, |
|
|
|
|
|
|
|
health: 'ok', |
|
|
|
|
|
|
|
query: 'count without (mode) (node_cpu_seconds_total{job="integrations/node_exporter"})', |
|
|
|
|
|
|
|
labels: { type: 'cpu' }, |
|
|
|
|
|
|
|
} satisfies RecordingRule, |
|
|
|
|
|
|
|
ruler: { |
|
|
|
|
|
|
|
record: 'instance:node_num_cpu:sum', |
|
|
|
|
|
|
|
expr: 'count without (mode) (node_cpu_seconds_total{job="integrations/node_exporter"})', |
|
|
|
|
|
|
|
labels: { type: 'cpu' }, |
|
|
|
|
|
|
|
} satisfies RulerRecordingRuleDTO, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
describe('hashRulerRule', () => { |
|
|
|
describe('hashRulerRule', () => { |
|
|
|
it('should not hash unknown rule types', () => { |
|
|
|
it('should not hash unknown rule types', () => { |
|
|
@ -18,8 +53,9 @@ describe('hashRulerRule', () => { |
|
|
|
expect(() => { |
|
|
|
expect(() => { |
|
|
|
// @ts-ignore
|
|
|
|
// @ts-ignore
|
|
|
|
hashRulerRule(unknownRule); |
|
|
|
hashRulerRule(unknownRule); |
|
|
|
}).toThrowError(); |
|
|
|
}).toThrow('Only recording and alerting ruler rules can be hashed'); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('should hash recording rules', () => { |
|
|
|
it('should hash recording rules', () => { |
|
|
|
const recordingRule: RulerRecordingRuleDTO = { |
|
|
|
const recordingRule: RulerRecordingRuleDTO = { |
|
|
|
record: 'instance:node_num_cpu:sum', |
|
|
|
record: 'instance:node_num_cpu:sum', |
|
|
@ -117,6 +153,48 @@ describe('hashRulerRule', () => { |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('hashRule', () => { |
|
|
|
|
|
|
|
it('should produce hashRulerRule compatible hashes for alerting rules', () => { |
|
|
|
|
|
|
|
const promHash = hashRule(alertingRule.prom); |
|
|
|
|
|
|
|
const rulerHash = hashRulerRule(alertingRule.ruler); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expect(promHash).toBe(rulerHash); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should produce hashRulerRule compatible hashes for recording rules', () => { |
|
|
|
|
|
|
|
const promHash = hashRule(recordingRule.prom); |
|
|
|
|
|
|
|
const rulerHash = hashRulerRule(recordingRule.ruler); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expect(promHash).toBe(rulerHash); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
describe('equal', () => { |
|
|
|
|
|
|
|
it('should return true for Prom and cloud identifiers with the same name, type, query and labels', () => { |
|
|
|
|
|
|
|
const promIdentifier: RuleIdentifier = { |
|
|
|
|
|
|
|
ruleSourceName: 'mimir-cloud', |
|
|
|
|
|
|
|
namespace: 'cloud-alerts', |
|
|
|
|
|
|
|
groupName: 'cpu-usage', |
|
|
|
|
|
|
|
ruleName: alertingRule.prom.name, |
|
|
|
|
|
|
|
ruleHash: hashRule(alertingRule.prom), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cloudIdentifier: RuleIdentifier = { |
|
|
|
|
|
|
|
ruleSourceName: 'mimir-cloud', |
|
|
|
|
|
|
|
namespace: 'cloud-alerts', |
|
|
|
|
|
|
|
groupName: 'cpu-usage', |
|
|
|
|
|
|
|
ruleName: alertingRule.ruler.alert, |
|
|
|
|
|
|
|
ruleHash: hashRulerRule(alertingRule.ruler), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const promToCloud = equal(promIdentifier, cloudIdentifier); |
|
|
|
|
|
|
|
const cloudToProm = equal(cloudIdentifier, promIdentifier); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expect(promToCloud).toBe(true); |
|
|
|
|
|
|
|
expect(cloudToProm).toBe(true); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
describe('useRuleIdFromPathname', () => { |
|
|
|
describe('useRuleIdFromPathname', () => { |
|
|
|
it('should return undefined when there is no id in params', () => { |
|
|
|
it('should return undefined when there is no id in params', () => { |
|
|
|
const { result } = renderHook(() => { |
|
|
|
const { result } = renderHook(() => { |
|
|
|