mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 16:10:43 +00:00
## Summary - Add cloud auth infrastructure (`cloud-env.js`, `cloud-auth.js`) for cloud/self-hosted switching - Centralize ingestion auth via `getAuthHeaders()` across all ingestion utilities (data, trace, service-graph, metrics) - Refactor `commonActions.js` from `exec(curl)` to `page.request.post()` for cloud cookie compatibility - Enhance `cloudLoginPage.js` + `page-manager.js` for conditional cloud page objects - Update `global-setup-alpha1.js` to fetch org identifier and passcode from cloud session - Fix `global-teardown.js` race condition with proper error handling - Replace `console.log` with `testLogger` in `commonActions.js`, `ingest-service-graph.spec.js`, `scripts/ingest-service-graph.js` - Replace raw selectors with POM methods (`applyQueryButton`, VRL editor, pagination, time range) - Add missing test tags to `searchpartition`, `unflattened`, `ingest-service-graph` specs - Fix `networkidle` → `domcontentloaded` across multiple spec files ## Companion PR - Enterprise: sets `IS_CLOUD=true` in alpha1 E2E workflow ## Test plan - [ ] Self-hosted E2E tests pass (no behavioral changes — only routing through POM + testLogger) - [ ] Alpha1 cloud E2E tests pass with cloud auth enabled - [ ] Verify no credential leaks in logs (passcode/password values never logged) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Shrinath Rao <undefined@users.noreply.github.com>
237 lines
8.0 KiB
JavaScript
237 lines
8.0 KiB
JavaScript
const testLogger = require('./test-logger.js');
|
|
const logsdata = require("../../../test-data/logs_data.json");
|
|
const { getAuthHeaders, getOrgIdentifier } = require('./cloud-auth.js');
|
|
|
|
/**
|
|
* Ingest test data into a stream via API using Node.js context (secure)
|
|
* @param {import('@playwright/test').Page} page - Playwright page object
|
|
* @param {string} streamName - Name of the stream to ingest data into (default: "e2e_automate")
|
|
* @returns {Promise<object>} - API response
|
|
*/
|
|
async function ingestTestData(page, streamName = "e2e_automate") {
|
|
const orgId = getOrgIdentifier();
|
|
const headers = getHeaders();
|
|
const baseUrl = process.env.INGESTION_URL.endsWith('/')
|
|
? process.env.INGESTION_URL.slice(0, -1)
|
|
: process.env.INGESTION_URL;
|
|
|
|
try {
|
|
const response = await page.request.post(`${baseUrl}/api/${orgId}/${streamName}/_json`, {
|
|
headers: headers,
|
|
data: logsdata
|
|
});
|
|
|
|
const responseData = await response.json().catch(() => ({ error: 'Failed to parse JSON' }));
|
|
testLogger.debug('Test data ingestion response', { response: responseData, streamName });
|
|
return responseData;
|
|
} catch (e) {
|
|
testLogger.debug('Test data ingestion error', { error: e.message, streamName });
|
|
return { error: e.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates authentication headers for API requests.
|
|
* Delegates to cloud-auth.js which handles both cloud (cookie-based) and
|
|
* self-hosted (Basic Auth) environments.
|
|
*
|
|
* @returns {Object} Headers object with appropriate auth for the environment
|
|
*/
|
|
function getHeaders() {
|
|
return getAuthHeaders();
|
|
}
|
|
|
|
/**
|
|
* Constructs the ingestion URL for a specific stream.
|
|
* @param {string} orgId - The organization ID
|
|
* @param {string} streamName - The name of the stream to ingest data into
|
|
* @returns {string} The complete ingestion URL
|
|
* @example
|
|
* const url = getIngestionUrl("default", "my_stream");
|
|
* // Returns: "http://localhost:5080/api/default/my_stream/_json"
|
|
*/
|
|
function getIngestionUrl(orgId, streamName) {
|
|
return `${process.env.INGESTION_URL}/api/${orgId}/${streamName}/_json`;
|
|
}
|
|
|
|
/**
|
|
* Sends an ingestion request using page.request API (secure, keeps credentials in Node.js context).
|
|
* @param {Page} page - Playwright page object
|
|
* @param {string} url - The ingestion URL
|
|
* @param {Object} payload - The data payload to ingest
|
|
* @param {Object} headers - Request headers including authentication
|
|
* @returns {Promise<Object>} The API response
|
|
* @example
|
|
* const response = await sendRequest(page, url, { log: "test" }, headers);
|
|
* // Returns: { code: 200, status: [{ name: "stream", successful: 1 }] }
|
|
*/
|
|
async function sendRequest(page, url, payload, headers) {
|
|
try {
|
|
const response = await page.request.post(url, {
|
|
headers: headers,
|
|
data: payload
|
|
});
|
|
return await response.json().catch(() => ({ error: 'Failed to parse JSON' }));
|
|
} catch (e) {
|
|
testLogger.debug('sendRequest error', { error: e.message, url });
|
|
return { error: e.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ingest custom data into a stream via API using Node.js context (secure)
|
|
* @param {import('@playwright/test').Page} page - Playwright page object
|
|
* @param {string} streamName - Name of the stream to ingest data into
|
|
* @param {Array} data - Array of log records to ingest
|
|
* @returns {Promise<object>} - API response with status and data
|
|
*/
|
|
async function ingestCustomData(page, streamName, data) {
|
|
const orgId = getOrgIdentifier();
|
|
const headers = getHeaders();
|
|
const baseUrl = process.env.INGESTION_URL.endsWith('/')
|
|
? process.env.INGESTION_URL.slice(0, -1)
|
|
: process.env.INGESTION_URL;
|
|
|
|
try {
|
|
// Use Playwright's request API to keep credentials in Node.js context
|
|
const response = await page.request.post(`${baseUrl}/api/${orgId}/${streamName}/_json`, {
|
|
headers: headers,
|
|
data: data
|
|
});
|
|
|
|
let responseData = null;
|
|
try {
|
|
responseData = await response.json();
|
|
} catch (e) {
|
|
responseData = { error: 'Failed to parse JSON response' };
|
|
}
|
|
|
|
testLogger.debug('Custom data ingestion response', { status: response.status(), streamName });
|
|
return {
|
|
status: response.status(),
|
|
data: responseData
|
|
};
|
|
} catch (e) {
|
|
testLogger.debug('Custom data ingestion error', { error: e.message, streamName });
|
|
return {
|
|
status: 500,
|
|
data: { error: e.message }
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable log patterns extraction on a stream via API using Node.js context (secure)
|
|
* @param {import('@playwright/test').Page} page - Playwright page object
|
|
* @param {string} streamName - Name of the stream
|
|
* @returns {Promise<object>} - API response with status and data
|
|
*/
|
|
async function enableLogPatternsExtraction(page, streamName) {
|
|
const orgId = getOrgIdentifier();
|
|
const headers = getHeaders();
|
|
const baseUrl = process.env.INGESTION_URL.endsWith('/')
|
|
? process.env.INGESTION_URL.slice(0, -1)
|
|
: process.env.INGESTION_URL;
|
|
|
|
const settingsPayload = {
|
|
enable_log_patterns_extraction: true
|
|
};
|
|
|
|
try {
|
|
// Use Playwright's request API to keep credentials in Node.js context
|
|
const response = await page.request.put(`${baseUrl}/api/${orgId}/streams/${streamName}/settings?type=logs`, {
|
|
headers: headers,
|
|
data: settingsPayload
|
|
});
|
|
|
|
let responseData = null;
|
|
try {
|
|
responseData = await response.json();
|
|
} catch (e) {
|
|
responseData = { error: 'Failed to parse JSON response' };
|
|
}
|
|
|
|
testLogger.debug('Enable log patterns extraction response', { status: response.status(), streamName, data: responseData });
|
|
return {
|
|
status: response.status(),
|
|
data: responseData
|
|
};
|
|
} catch (e) {
|
|
testLogger.debug('Enable log patterns extraction error', { error: e.message, streamName });
|
|
return {
|
|
status: 500,
|
|
data: { error: e.message }
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for stream data to be indexed by polling the search API
|
|
* Replaces arbitrary waitForTimeout with explicit condition check
|
|
* @param {import('@playwright/test').Page} page - Playwright page object
|
|
* @param {string} streamName - Name of the stream to check
|
|
* @param {number} expectedMinCount - Minimum expected record count (default: 1)
|
|
* @param {number} maxWaitMs - Maximum wait time in ms (default: 30000)
|
|
* @param {number} pollIntervalMs - Polling interval in ms (default: 2000)
|
|
* @returns {Promise<boolean>} - True if data found, false if timed out
|
|
*/
|
|
async function waitForStreamData(page, streamName, expectedMinCount = 1, maxWaitMs = 30000, pollIntervalMs = 2000) {
|
|
const orgId = getOrgIdentifier();
|
|
const headers = getHeaders();
|
|
const baseUrl = process.env.INGESTION_URL.endsWith('/')
|
|
? process.env.INGESTION_URL.slice(0, -1)
|
|
: process.env.INGESTION_URL;
|
|
|
|
const startTime = Date.now();
|
|
const endTime = Date.now() * 1000; // microseconds
|
|
const startTimeUs = (Date.now() - 3600000) * 1000; // 1 hour ago in microseconds
|
|
|
|
const searchPayload = {
|
|
query: {
|
|
sql: `SELECT COUNT(*) as count FROM "${streamName}"`,
|
|
start_time: startTimeUs,
|
|
end_time: endTime,
|
|
from: 0,
|
|
size: 1
|
|
}
|
|
};
|
|
|
|
while (Date.now() - startTime < maxWaitMs) {
|
|
try {
|
|
const response = await page.request.post(`${baseUrl}/api/${orgId}/_search?type=logs`, {
|
|
headers: headers,
|
|
data: searchPayload
|
|
});
|
|
|
|
if (response.status() === 200) {
|
|
const data = await response.json().catch(() => null);
|
|
const count = data?.hits?.[0]?.count || data?.total || 0;
|
|
|
|
if (count >= expectedMinCount) {
|
|
testLogger.debug('Stream data indexed', { streamName, count, waitedMs: Date.now() - startTime });
|
|
return true;
|
|
}
|
|
testLogger.debug('Polling for stream data', { streamName, currentCount: count, expectedMinCount });
|
|
}
|
|
} catch (e) {
|
|
testLogger.debug('Stream data poll error', { error: e.message, streamName });
|
|
}
|
|
|
|
// Wait before next poll
|
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
}
|
|
|
|
testLogger.warn('Stream data poll timed out', { streamName, maxWaitMs });
|
|
return false;
|
|
}
|
|
|
|
module.exports = {
|
|
ingestTestData,
|
|
getHeaders,
|
|
getIngestionUrl,
|
|
sendRequest,
|
|
ingestCustomData,
|
|
enableLogPatternsExtraction,
|
|
waitForStreamData
|
|
};
|