Scope NEAR upstream pauses to tracked assets
Some checks failed
deploy / deploy (push) Failing after 37s
Some checks failed
deploy / deploy (push) Failing after 37s
Proof: npm test; npm run operator-dashboard:build; live NEAR Intents status API normalized the current HOT destination-chain incident as operational for btc:mainnet and eth:100 tracked assets. Assumptions: BSC, MONAD, XLAYER, PLASMA, POL, TON, OP, AVAX, STELLAR, and ADI incidents do not block our configured BTC/Gnosis NEAR Intents quote or executor paths unless the official incident also names core 1Click, solver, message bus, protocol-wide swaps, BTC, or Gnosis scope. Still fake: Status relevance is text/service scoped to configured assets; it is not backed by a venue-provided per-asset machine-readable impact matrix.
This commit is contained in:
parent
4adc705a2b
commit
754a95c6d2
6 changed files with 320 additions and 21 deletions
|
|
@ -576,6 +576,7 @@ async function loadNearIntentsStatus() {
|
||||||
postsResponse,
|
postsResponse,
|
||||||
postEnumsResponse,
|
postEnumsResponse,
|
||||||
observedAt: new Date().toISOString(),
|
observedAt: new Date().toISOString(),
|
||||||
|
trackedAssets: config.trackedAssets,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -631,6 +631,7 @@ async function pollNearIntentsEnvironmentStatus() {
|
||||||
postsResponse,
|
postsResponse,
|
||||||
postEnumsResponse,
|
postEnumsResponse,
|
||||||
observedAt,
|
observedAt,
|
||||||
|
trackedAssets: config.trackedAssets,
|
||||||
});
|
});
|
||||||
|
|
||||||
state.near_intents_status = normalized;
|
state.near_intents_status = normalized;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ export function normalizeNearIntentsStatus({
|
||||||
servicesResponse = null,
|
servicesResponse = null,
|
||||||
postEnumsResponse = null,
|
postEnumsResponse = null,
|
||||||
observedAt = new Date().toISOString(),
|
observedAt = new Date().toISOString(),
|
||||||
|
trackedAssets = [],
|
||||||
|
relevantChains = [],
|
||||||
|
relevantAssetIds = [],
|
||||||
|
relevantServiceNames = [],
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const posts = Array.isArray(postsResponse?.posts)
|
const posts = Array.isArray(postsResponse?.posts)
|
||||||
? postsResponse.posts
|
? postsResponse.posts
|
||||||
|
|
@ -41,31 +45,60 @@ export function normalizeNearIntentsStatus({
|
||||||
);
|
);
|
||||||
const serviceById = new Map(services.map((entry) => [entry.id, entry]));
|
const serviceById = new Map(services.map((entry) => [entry.id, entry]));
|
||||||
|
|
||||||
const incidents = posts
|
const scope = buildRelevanceScope({
|
||||||
|
trackedAssets,
|
||||||
|
relevantChains,
|
||||||
|
relevantAssetIds,
|
||||||
|
relevantServiceNames,
|
||||||
|
});
|
||||||
|
const activeIncidents = posts
|
||||||
.map((post) => normalizePost(post, { statusById, severityById, serviceById }))
|
.map((post) => normalizePost(post, { statusById, severityById, serviceById }))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.filter((post) => post.active);
|
.filter((post) => post.active);
|
||||||
|
const relevantIncidents = activeIncidents.filter((incident) => (
|
||||||
|
isRelevantIncident(incident, scope)
|
||||||
|
));
|
||||||
|
const unrelatedIncidents = activeIncidents.filter((incident) => (
|
||||||
|
!isRelevantIncident(incident, scope)
|
||||||
|
));
|
||||||
|
|
||||||
const affectedServices = [...new Set(
|
const affectedServices = [...new Set(
|
||||||
incidents.flatMap((incident) => incident.impacts.map((impact) => impact.service_name || impact.service_id)),
|
relevantIncidents.flatMap((incident) => incident.impacts.map((impact) => (
|
||||||
|
impact.service_name || impact.service_id
|
||||||
|
))),
|
||||||
)].filter(Boolean);
|
)].filter(Boolean);
|
||||||
const primaryIncident = incidents[0] || null;
|
const observedAffectedServices = [...new Set(
|
||||||
const status = incidents.length > 0 ? 'disrupted' : 'operational';
|
activeIncidents.flatMap((incident) => incident.impacts.map((impact) => (
|
||||||
const label = incidents.length > 0 ? 'upstream paused' : 'operational';
|
impact.service_name || impact.service_id
|
||||||
const decisiveReason = primaryIncident
|
))),
|
||||||
? [primaryIncident.title, primaryIncident.message_text].filter(Boolean).join(': ')
|
)].filter(Boolean);
|
||||||
: 'NEAR Intents status page reports no active incident.';
|
const primaryIncident = relevantIncidents[0] || null;
|
||||||
const quotingStopped = incidents.some((incident) => /1click|quoting|solver|swap/i.test(
|
const primaryUnrelatedIncident = unrelatedIncidents[0] || null;
|
||||||
`${incident.title || ''} ${incident.message_text || ''}`,
|
const status = relevantIncidents.length > 0 ? 'disrupted' : 'operational';
|
||||||
|
const label = relevantIncidents.length > 0 ? 'upstream paused' : 'operational';
|
||||||
|
const decisiveReason = buildDecisiveReason({
|
||||||
|
primaryIncident,
|
||||||
|
primaryUnrelatedIncident,
|
||||||
|
activeIncidentCount: activeIncidents.length,
|
||||||
|
scope,
|
||||||
|
});
|
||||||
|
const quotingStopped = relevantIncidents.some((incident) => /1click|quoting|solver|swap/i.test(
|
||||||
|
incidentText(incident),
|
||||||
));
|
));
|
||||||
const normalized = {
|
const normalized = {
|
||||||
observed_at: observedAt,
|
observed_at: observedAt,
|
||||||
source: 'near_intents_status_page',
|
source: 'near_intents_status_page',
|
||||||
status,
|
status,
|
||||||
label,
|
label,
|
||||||
current_incident_count: incidents.length,
|
current_incident_count: relevantIncidents.length,
|
||||||
current_incidents: incidents,
|
current_incidents: relevantIncidents,
|
||||||
affected_services: affectedServices,
|
affected_services: affectedServices,
|
||||||
|
observed_incident_count: activeIncidents.length,
|
||||||
|
observed_incidents: activeIncidents,
|
||||||
|
observed_affected_services: observedAffectedServices,
|
||||||
|
unrelated_incident_count: unrelatedIncidents.length,
|
||||||
|
unrelated_incidents: unrelatedIncidents,
|
||||||
|
relevance_scope: scope.publicScope,
|
||||||
quoting_stopped: quotingStopped,
|
quoting_stopped: quotingStopped,
|
||||||
decisive_reason: decisiveReason,
|
decisive_reason: decisiveReason,
|
||||||
};
|
};
|
||||||
|
|
@ -95,6 +128,12 @@ export function buildNearIntentsStatusEventPayload(status, {
|
||||||
current_incident_count: status.current_incident_count || 0,
|
current_incident_count: status.current_incident_count || 0,
|
||||||
current_incidents: status.current_incidents || [],
|
current_incidents: status.current_incidents || [],
|
||||||
affected_services: status.affected_services || [],
|
affected_services: status.affected_services || [],
|
||||||
|
observed_incident_count: status.observed_incident_count || status.current_incident_count || 0,
|
||||||
|
observed_incidents: status.observed_incidents || status.current_incidents || [],
|
||||||
|
observed_affected_services: status.observed_affected_services || status.affected_services || [],
|
||||||
|
unrelated_incident_count: status.unrelated_incident_count || 0,
|
||||||
|
unrelated_incidents: status.unrelated_incidents || [],
|
||||||
|
relevance_scope: status.relevance_scope || null,
|
||||||
quoting_stopped: status.quoting_stopped ?? null,
|
quoting_stopped: status.quoting_stopped ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -104,21 +143,160 @@ export function buildNearIntentsStatusFingerprint(status = {}) {
|
||||||
status: status.status || 'unknown',
|
status: status.status || 'unknown',
|
||||||
label: status.label || null,
|
label: status.label || null,
|
||||||
decisive_reason: status.decisive_reason || null,
|
decisive_reason: status.decisive_reason || null,
|
||||||
current_incidents: (status.current_incidents || []).map((incident) => ({
|
current_incidents: (status.current_incidents || []).map(stableIncident),
|
||||||
id: incident.id || null,
|
observed_incidents: (status.observed_incidents || status.current_incidents || []).map(stableIncident),
|
||||||
title: incident.title || null,
|
|
||||||
status: incident.status || null,
|
|
||||||
severity: incident.severity || null,
|
|
||||||
last_update_at: incident.last_update_at || null,
|
|
||||||
message_text: incident.message_text || null,
|
|
||||||
impacts: incident.impacts || [],
|
|
||||||
})),
|
|
||||||
affected_services: [...(status.affected_services || [])].sort(),
|
affected_services: [...(status.affected_services || [])].sort(),
|
||||||
|
observed_affected_services: [...(status.observed_affected_services || [])].sort(),
|
||||||
|
unrelated_incident_count: status.unrelated_incident_count || 0,
|
||||||
|
relevance_scope: status.relevance_scope || null,
|
||||||
quoting_stopped: status.quoting_stopped ?? null,
|
quoting_stopped: status.quoting_stopped ?? null,
|
||||||
};
|
};
|
||||||
return crypto.createHash('sha256').update(JSON.stringify(stable)).digest('hex');
|
return crypto.createHash('sha256').update(JSON.stringify(stable)).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stableIncident(incident) {
|
||||||
|
return {
|
||||||
|
id: incident.id || null,
|
||||||
|
title: incident.title || null,
|
||||||
|
status: incident.status || null,
|
||||||
|
severity: incident.severity || null,
|
||||||
|
last_update_at: incident.last_update_at || null,
|
||||||
|
message_text: incident.message_text || null,
|
||||||
|
impacts: incident.impacts || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDecisiveReason({
|
||||||
|
primaryIncident,
|
||||||
|
primaryUnrelatedIncident,
|
||||||
|
activeIncidentCount,
|
||||||
|
scope,
|
||||||
|
}) {
|
||||||
|
if (primaryIncident) {
|
||||||
|
return [primaryIncident.title, primaryIncident.message_text].filter(Boolean).join(': ');
|
||||||
|
}
|
||||||
|
if (primaryUnrelatedIncident) {
|
||||||
|
const scopeText = scope.publicScope.terms.length
|
||||||
|
? scope.publicScope.terms.slice(0, 8).join(', ')
|
||||||
|
: 'unscoped deployment';
|
||||||
|
return [
|
||||||
|
`NEAR Intents status page has ${activeIncidentCount} active incident(s), but none match this deployment scope (${scopeText}).`,
|
||||||
|
[primaryUnrelatedIncident.title, primaryUnrelatedIncident.message_text].filter(Boolean).join(': '),
|
||||||
|
].filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
return 'NEAR Intents status page reports no active incident.';
|
||||||
|
}
|
||||||
|
|
||||||
|
const CORE_INTENTS_SERVICE_PATTERN = /\b(1click|solver|message bus)\b/i;
|
||||||
|
const GLOBAL_INTENTS_DISRUPTION_PATTERN = /\b(1click|quoting|solver|message bus|protocol is paused|swaps are paused|all swaps)\b/i;
|
||||||
|
|
||||||
|
function isRelevantIncident(incident, scope) {
|
||||||
|
if (scope.unfiltered) return true;
|
||||||
|
|
||||||
|
const text = incidentText(incident);
|
||||||
|
if (GLOBAL_INTENTS_DISRUPTION_PATTERN.test(text)) return true;
|
||||||
|
|
||||||
|
const impactedServices = (incident.impacts || [])
|
||||||
|
.map((impact) => `${impact.service_name || ''} ${impact.service_id || ''}`.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (impactedServices.some((service) => CORE_INTENTS_SERVICE_PATTERN.test(service))) return true;
|
||||||
|
if (impactedServices.some((service) => scope.serviceNames.has(normalizeToken(service)))) return true;
|
||||||
|
|
||||||
|
return scope.termPatterns.some((pattern) => pattern.test(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
function incidentText(incident) {
|
||||||
|
return [
|
||||||
|
incident?.title,
|
||||||
|
incident?.message_text,
|
||||||
|
...(incident?.impacts || []).map((impact) => impact.service_name || impact.service_id),
|
||||||
|
].filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRelevanceScope({
|
||||||
|
trackedAssets = [],
|
||||||
|
relevantChains = [],
|
||||||
|
relevantAssetIds = [],
|
||||||
|
relevantServiceNames = [],
|
||||||
|
} = {}) {
|
||||||
|
const assets = Array.isArray(trackedAssets) ? trackedAssets : [];
|
||||||
|
const configuredChains = Array.isArray(relevantChains) ? relevantChains : [relevantChains];
|
||||||
|
const configuredAssetIds = Array.isArray(relevantAssetIds) ? relevantAssetIds : [relevantAssetIds];
|
||||||
|
const configuredServiceNames = Array.isArray(relevantServiceNames)
|
||||||
|
? relevantServiceNames
|
||||||
|
: [relevantServiceNames];
|
||||||
|
const chains = unique([
|
||||||
|
...configuredChains,
|
||||||
|
...assets.map((asset) => asset?.chain),
|
||||||
|
]);
|
||||||
|
const assetIds = unique([
|
||||||
|
...configuredAssetIds,
|
||||||
|
...assets.map((asset) => asset?.assetId),
|
||||||
|
]);
|
||||||
|
const serviceNames = new Set(configuredServiceNames.map(normalizeToken).filter(Boolean));
|
||||||
|
const terms = unique([
|
||||||
|
...chains.flatMap(chainTerms),
|
||||||
|
...assetIds.flatMap(assetTerms),
|
||||||
|
]).map(normalizeToken).filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
unfiltered: assets.length === 0
|
||||||
|
&& configuredChains.filter(Boolean).length === 0
|
||||||
|
&& configuredAssetIds.filter(Boolean).length === 0
|
||||||
|
&& configuredServiceNames.filter(Boolean).length === 0,
|
||||||
|
termPatterns: terms.map(termPattern),
|
||||||
|
serviceNames,
|
||||||
|
publicScope: {
|
||||||
|
chains,
|
||||||
|
asset_ids: assetIds,
|
||||||
|
service_names: [...serviceNames],
|
||||||
|
terms,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function chainTerms(chain) {
|
||||||
|
const normalized = String(chain || '').trim().toLowerCase();
|
||||||
|
const terms = [normalized];
|
||||||
|
if (normalized === 'btc:mainnet') terms.push('btc', 'bitcoin');
|
||||||
|
const [, evmChainId] = normalized.match(/^eth:(\d+)$/) || [];
|
||||||
|
if (evmChainId === '1') terms.push('ethereum', 'eth mainnet');
|
||||||
|
if (evmChainId === '100') terms.push('gnosis', 'gno', 'xdai');
|
||||||
|
return terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assetTerms(assetId) {
|
||||||
|
const normalized = String(assetId || '').trim().toLowerCase();
|
||||||
|
const terms = [normalized];
|
||||||
|
if (normalized.includes('nbtc') || normalized.includes('btc.')) {
|
||||||
|
terms.push('btc', 'bitcoin');
|
||||||
|
}
|
||||||
|
if (normalized.includes('gnosis')) {
|
||||||
|
terms.push('gnosis', 'gno', 'xdai');
|
||||||
|
}
|
||||||
|
return terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
function termPattern(term) {
|
||||||
|
const escaped = escapeRegExp(term);
|
||||||
|
if (/^[a-z0-9]+$/i.test(term)) {
|
||||||
|
return new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`, 'i');
|
||||||
|
}
|
||||||
|
return new RegExp(escaped, 'i');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeToken(value) {
|
||||||
|
return String(value || '').trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unique(values) {
|
||||||
|
return [...new Set((values || []).filter(Boolean))];
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(value) {
|
||||||
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
function normalizePost(post, { statusById, severityById, serviceById }) {
|
function normalizePost(post, { statusById, severityById, serviceById }) {
|
||||||
if (!post || typeof post !== 'object') return null;
|
if (!post || typeof post !== 'object') return null;
|
||||||
const latestUpdate = post.latest_update || [...(post.updates || [])].pop() || {};
|
const latestUpdate = post.latest_update || [...(post.updates || [])].pop() || {};
|
||||||
|
|
|
||||||
|
|
@ -1625,6 +1625,9 @@ function resolveServiceUpstreamStatus(service, nearIntentsStatus) {
|
||||||
decisive_reason: nearIntentsStatus.decisive_reason || null,
|
decisive_reason: nearIntentsStatus.decisive_reason || null,
|
||||||
current_incident_count: nearIntentsStatus.current_incident_count || 0,
|
current_incident_count: nearIntentsStatus.current_incident_count || 0,
|
||||||
affected_services: nearIntentsStatus.affected_services || [],
|
affected_services: nearIntentsStatus.affected_services || [],
|
||||||
|
observed_incident_count: nearIntentsStatus.observed_incident_count ?? null,
|
||||||
|
observed_affected_services: nearIntentsStatus.observed_affected_services || [],
|
||||||
|
unrelated_incident_count: nearIntentsStatus.unrelated_incident_count || 0,
|
||||||
quoting_stopped: nearIntentsStatus.quoting_stopped ?? null,
|
quoting_stopped: nearIntentsStatus.quoting_stopped ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,21 @@ const servicesResponse = {
|
||||||
services: [
|
services: [
|
||||||
{ id: 'PXQFSY1', name: 'Cross-Chain Bridging', display_name: 'Cross-Chain Bridging' },
|
{ id: 'PXQFSY1', name: 'Cross-Chain Bridging', display_name: 'Cross-Chain Bridging' },
|
||||||
{ id: 'PLT88AT', name: 'Solvers Network', display_name: 'Solvers Network' },
|
{ id: 'PLT88AT', name: 'Solvers Network', display_name: 'Solvers Network' },
|
||||||
|
{ id: 'PNEJBRE', name: 'Other Blockchains', display_name: 'Other Blockchains' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const trackedAssets = [
|
||||||
|
{
|
||||||
|
assetId: 'nep141:nbtc.bridge.near',
|
||||||
|
chain: 'btc:mainnet',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near',
|
||||||
|
chain: 'eth:100',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
test('NEAR Intents status normalizer exposes current quoting disruption as upstream paused evidence', () => {
|
test('NEAR Intents status normalizer exposes current quoting disruption as upstream paused evidence', () => {
|
||||||
const normalized = normalizeNearIntentsStatus({
|
const normalized = normalizeNearIntentsStatus({
|
||||||
observedAt: '2026-04-16T12:40:00.000Z',
|
observedAt: '2026-04-16T12:40:00.000Z',
|
||||||
|
|
@ -85,6 +97,40 @@ test('resolved NEAR Intents status posts do not make the relay look disrupted',
|
||||||
assert.match(normalized.decisive_reason, /no active incident/i);
|
assert.match(normalized.decisive_reason, /no active incident/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('scoped NEAR Intents status ignores unrelated paused destination chains', () => {
|
||||||
|
const normalized = normalizeNearIntentsStatus({
|
||||||
|
observedAt: '2026-05-07T15:55:00.000Z',
|
||||||
|
servicesResponse,
|
||||||
|
postEnumsResponse,
|
||||||
|
trackedAssets,
|
||||||
|
postsResponse: {
|
||||||
|
posts: [{
|
||||||
|
id: 'PO2LXSS',
|
||||||
|
title: 'BSC, TON, XML destinations are temporarily paused',
|
||||||
|
post_type: 'incident',
|
||||||
|
latest_update: {
|
||||||
|
status_id: 'PSCS3IV',
|
||||||
|
severity_id: 'P187122',
|
||||||
|
reported_at: 1778165160000,
|
||||||
|
impacts: [{ service_id: 'PNEJBRE', severity_id: 'PCIGMKW' }],
|
||||||
|
message: '<p>HOT bridge is having a reliability incident. Full list of chains: BSC, MONAD, XLAYER, PLASMA, POL, TON, OP, AVAX, STELLAR, ADI</p>',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(normalized.status, 'operational');
|
||||||
|
assert.equal(normalized.label, 'operational');
|
||||||
|
assert.equal(normalized.current_incident_count, 0);
|
||||||
|
assert.equal(normalized.observed_incident_count, 1);
|
||||||
|
assert.equal(normalized.unrelated_incident_count, 1);
|
||||||
|
assert.equal(normalized.quoting_stopped, false);
|
||||||
|
assert.deepEqual(normalized.affected_services, []);
|
||||||
|
assert.deepEqual(normalized.observed_affected_services, ['Other Blockchains']);
|
||||||
|
assert.match(normalized.decisive_reason, /none match this deployment scope/);
|
||||||
|
assert.match(normalized.decisive_reason, /BSC, TON, XML/);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('NEAR Intents status fingerprint is stable across polls and changes on official updates', () => {
|
test('NEAR Intents status fingerprint is stable across polls and changes on official updates', () => {
|
||||||
const first = normalizeNearIntentsStatus({
|
const first = normalizeNearIntentsStatus({
|
||||||
|
|
|
||||||
|
|
@ -1735,6 +1735,76 @@ test('dashboard surfaces NEAR upstream disruption without calling submitted work
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('dashboard does not pause relay services for unrelated destination-chain incident', () => {
|
||||||
|
const config = buildConfig();
|
||||||
|
const nearIntentsStatus = {
|
||||||
|
source: 'near_intents_status_page',
|
||||||
|
status: 'operational',
|
||||||
|
label: 'operational',
|
||||||
|
observed_at: '2026-05-07T15:55:00.000Z',
|
||||||
|
decisive_reason: 'NEAR Intents status page has 1 active incident(s), but none match this deployment scope.',
|
||||||
|
current_incident_count: 0,
|
||||||
|
affected_services: [],
|
||||||
|
observed_incident_count: 1,
|
||||||
|
observed_affected_services: ['Other Blockchains'],
|
||||||
|
unrelated_incident_count: 1,
|
||||||
|
quoting_stopped: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dashboard = buildDashboardBootstrap({
|
||||||
|
config,
|
||||||
|
auth: { authenticated: true },
|
||||||
|
portfolioMetric: null,
|
||||||
|
inventorySnapshot: null,
|
||||||
|
marketPrice: null,
|
||||||
|
recentQuotes: [],
|
||||||
|
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
|
||||||
|
submissionSummary: { total: 0, last_submission_at: null },
|
||||||
|
fundingObservations: [],
|
||||||
|
recentDepositStatuses: [],
|
||||||
|
recentTradeDecisions: [],
|
||||||
|
recentExecuteTradeCommands: [],
|
||||||
|
recentExecutionResults: [],
|
||||||
|
recentQuoteOutcomes: [],
|
||||||
|
recentIntentRequests: [],
|
||||||
|
recentAlertTransitions: [],
|
||||||
|
nearIntentsStatus,
|
||||||
|
serviceSnapshots: [
|
||||||
|
{
|
||||||
|
service: 'near-intents-ingest',
|
||||||
|
label: 'NEAR Intents Ingest',
|
||||||
|
base_url: 'http://near-intents-ingest',
|
||||||
|
reachable: true,
|
||||||
|
health: { ok: true },
|
||||||
|
state: { ingest: { connected: true, last_message_at: '2026-05-07T15:54:59.000Z' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'trade-executor',
|
||||||
|
label: 'Trade Executor',
|
||||||
|
base_url: 'http://trade-executor',
|
||||||
|
reachable: true,
|
||||||
|
health: { ok: true },
|
||||||
|
state: { relay: { connected: true, last_message_at: '2026-05-07T15:54:59.000Z' } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(dashboard.status_bar.near_intents_upstream_status, 'operational');
|
||||||
|
assert.match(dashboard.status_bar.near_intents_upstream_reason, /none match this deployment scope/);
|
||||||
|
|
||||||
|
const services = Object.fromEntries(
|
||||||
|
dashboard.system.service_health.map((service) => [service.service, service]),
|
||||||
|
);
|
||||||
|
assert.equal(services['near-intents-ingest'].health_status, 'online');
|
||||||
|
assert.equal(services['near-intents-ingest'].health_label, 'online');
|
||||||
|
assert.equal(services['near-intents-ingest'].health_ok, true);
|
||||||
|
assert.equal(services['trade-executor'].health_status, 'online');
|
||||||
|
assert.equal(services['trade-executor'].health_label, 'online');
|
||||||
|
assert.equal(services['trade-executor'].upstream_status.status, 'operational');
|
||||||
|
assert.equal(services['trade-executor'].upstream_status.unrelated_incident_count, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('bootstrap exposes deduped environment status history as environmental conditions', () => {
|
test('bootstrap exposes deduped environment status history as environmental conditions', () => {
|
||||||
const config = buildConfig();
|
const config = buildConfig();
|
||||||
const dashboard = buildDashboardBootstrap({
|
const dashboard = buildDashboardBootstrap({
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue