1+ /**
2+ * Publisher Plugin Performance Test
3+ *
4+ * Tests 10 knowledge asset publications with comprehensive metrics.
5+ *
6+ * REQUIRED ENVIRONMENT VARIABLES (add to .env.testing.testnet.local):
7+ * - PUBLISHING_TIMEOUT_MINUTES=5 # Timeout per publish (5 min for testing, 15 min production)
8+ * - HEALTH_CHECK_INTERVAL_MS=10000 # Check every 10 seconds (60s production)
9+ * - REDIS_HOST=localhost
10+ * - DKGP_DATABASE_URL=mysql://root:@127.0.0.1:3306/dkg_publisher_db
11+ *
12+ * See PUBLISHER_TEST_SETUP.md for detailed configuration instructions.
13+ *
14+ * Test Duration:
15+ * - Best case: 20-50 minutes (all succeed quickly)
16+ * - Worst case: 50 minutes (all timeout at 5 min with maxAttempts=1)
17+ */
18+
119const { expect } = require ( "@playwright/test" ) ;
220const { test } = require ( "@playwright/test" ) ;
321const { Base } = require ( "../utils/base" ) ;
422const { LoginPage } = require ( "../pages/loginPage" ) ;
523const { ChatbotPage } = require ( "../pages/chatbotPage" ) ;
624const dotenv = require ( "dotenv" ) ;
725const path = require ( "path" ) ;
26+ const mysql = require ( "mysql2/promise" ) ;
27+ const Redis = require ( "ioredis" ) ;
828
929let base ;
1030let loginPage ;
1131let chatbotPage ;
1232
33+ // Disable retries for this test file (we test 10 assets in one run)
34+ test . describe . configure ( { retries : 0 } ) ;
35+
1336function loadEnvFile ( envFile ) {
1437 dotenv . config ( {
1538 path : path . join ( __dirname , ".." , envFile ) ,
1639 override : true ,
1740 } ) ;
1841}
1942
43+ /**
44+ * Clean up Redis and MySQL before test suite starts
45+ * Ensures each test run starts with a fresh state
46+ */
47+ test . beforeAll ( async ( ) => {
48+ console . log ( "\n🧹 Cleaning up test environment..." ) ;
49+
50+ try {
51+ // Clear Redis
52+ const redis = new Redis ( {
53+ host : process . env . REDIS_HOST || "localhost" ,
54+ port : parseInt ( process . env . REDIS_PORT || "6379" ) ,
55+ retryStrategy : ( ) => null , // Don't retry on failure
56+ } ) ;
57+
58+ await redis . flushall ( ) ;
59+ console . log ( "✅ Redis cleared" ) ;
60+ await redis . quit ( ) ;
61+ } catch ( error ) {
62+ console . warn ( "⚠️ Redis cleanup failed (may not be running):" , error . message ) ;
63+ }
64+
65+ try {
66+ // Clear MySQL
67+ const dbUrl = process . env . DKGP_DATABASE_URL || "mysql://root:@127.0.0.1:3306/dkg_publisher_db" ;
68+ const match = dbUrl . match ( / m y s q l : \/ \/ ( [ ^ : ] + ) : ( [ ^ @ ] * ) @ ( [ ^ : ] + ) : ( \d + ) \/ ( .+ ) / ) ;
69+
70+ if ( ! match ) {
71+ throw new Error ( "Invalid database URL format" ) ;
72+ }
73+
74+ const [ , user , password , host , port , database ] = match ;
75+
76+ const connection = await mysql . createConnection ( {
77+ host,
78+ port : parseInt ( port ) ,
79+ user,
80+ password : password || undefined ,
81+ database,
82+ } ) ;
83+
84+ await connection . execute ( "DELETE FROM publishing_attempts" ) ;
85+ await connection . execute ( "DELETE FROM assets" ) ;
86+ await connection . execute ( "UPDATE wallets SET is_locked = 0, locked_by = NULL, locked_at = NULL" ) ;
87+ await connection . execute ( "ALTER TABLE assets AUTO_INCREMENT = 1" ) ;
88+
89+ console . log ( "✅ Database cleared (assets, attempts, unlocked wallets)" ) ;
90+ await connection . end ( ) ;
91+ } catch ( error ) {
92+ console . warn ( "⚠️ Database cleanup failed:" , error . message ) ;
93+ }
94+
95+ console . log ( "🚀 Test environment ready - starting with clean slate\n" ) ;
96+ } ) ;
97+
2098test . beforeEach ( async ( { page } ) => {
2199 base = new Base ( page ) ;
22100 await base . goToWebsite ( ) ;
@@ -63,8 +141,9 @@ test("Publisher Plugin - Performance Test: Publish 10 Assets with Metrics", asyn
63141 const dateCreated = new Date ( ) . toISOString ( ) ;
64142 const contentId = `urn:publisher-test:asset:${ timestamp } -${ i } ` ;
65143
66- const publishMessage = `Register this Knowledge Asset as PRIVATE using the knowledge-asset-publish tool:
144+ const publishMessage = `Register this Knowledge Asset using the knowledge-asset-publish tool with these settings :
67145
146+ Content:
68147{
69148 "@context": {
70149 "@vocab": "https://schema.org/"
@@ -77,7 +156,9 @@ test("Publisher Plugin - Performance Test: Publish 10 Assets with Metrics", asyn
77156 "testNumber": ${ i }
78157}
79158
80- Privacy: private
159+ Settings:
160+ - privacy: private
161+ - maxAttempts: 1
81162` ;
82163
83164 const testStart = Date . now ( ) ;
@@ -92,21 +173,35 @@ Privacy: private
92173 // Send publish request
93174 await chatbotPage . sendMessage ( publishMessage ) ;
94175
95- // Wait for "Asset registered" response and check for "queued" confirmation (no timeout)
96- await page . waitForFunction (
97- ( ) => {
98- const messages = Array . from (
99- document . querySelectorAll ( '[data-testid="chat-message-text"]' ) ,
100- ) ;
101- return messages . some ( ( msg ) => {
102- const text = msg . textContent || "" ;
103- return / q u e u e d f o r p u b l i s h i n g / i. test ( text ) ||
104- / A s s e t r e g i s t e r e d f o r p u b l i s h i n g / i. test ( text ) ;
105- } ) ;
106- } ,
107- { } ,
108- { timeout : 0 } ,
109- ) ;
176+ // Wait for "Asset registered" response and check for "queued" confirmation
177+ try {
178+ await page . waitForFunction (
179+ ( ) => {
180+ const messages = Array . from (
181+ document . querySelectorAll ( '[data-testid="chat-message-text"]' ) ,
182+ ) ;
183+ return messages . some ( ( msg ) => {
184+ const text = msg . textContent || "" ;
185+ return / q u e u e d f o r p u b l i s h i n g / i. test ( text ) ||
186+ / A s s e t r e g i s t e r e d f o r p u b l i s h i n g / i. test ( text ) ||
187+ / r e g i s t e r e d .* p u b l i s h i n g / i. test ( text ) ;
188+ } ) ;
189+ } ,
190+ { } ,
191+ { timeout : 30000 } , // 30 second timeout
192+ ) ;
193+ } catch ( timeoutError ) {
194+ // Log all messages to see what we actually got
195+ const allMessages = await page . $$eval (
196+ '[data-testid="chat-message-text"]' ,
197+ ( elements ) => elements . map ( ( el ) => el . textContent || "" )
198+ ) ;
199+ console . log ( ` ❌ Timeout waiting for queued confirmation. Messages received:` ) ;
200+ allMessages . forEach ( ( msg , idx ) => {
201+ console . log ( ` ${ idx + 1 } . ${ msg . substring ( 0 , 200 ) } ` ) ;
202+ } ) ;
203+ throw new Error ( `Asset not queued - timeout waiting for confirmation. Last message: ${ allMessages [ allMessages . length - 1 ] ?. substring ( 0 , 200 ) } ` ) ;
204+ }
110205
111206 console . log ( ` ✅ Asset queued with Content ID: ${ contentId } ` ) ;
112207 console . log ( ` ⏳ Polling status via chatbot until published or failed (no timeout)...` ) ;
@@ -125,19 +220,8 @@ Privacy: private
125220 // Ask chatbot for status
126221 const statusQuery = `What is the status of ${ contentId } ?` ;
127222
128- // Add timeout wrapper for sendMessage to prevent indefinite hangs
129- const sendPromise = chatbotPage . sendMessage ( statusQuery ) ;
130- const sendTimeout = new Promise ( ( _ , reject ) =>
131- setTimeout ( ( ) => reject ( new Error ( 'sendMessage timeout after 30s' ) ) , 30000 )
132- ) ;
133- await Promise . race ( [ sendPromise , sendTimeout ] ) ;
134-
135- // Add timeout wrapper for waitForResponse to prevent indefinite hangs
136- const responsePromise = chatbotPage . waitForResponse ( statusQuery ) ;
137- const responseTimeout = new Promise ( ( _ , reject ) =>
138- setTimeout ( ( ) => reject ( new Error ( 'waitForResponse timeout after 60s' ) ) , 60000 )
139- ) ;
140- await Promise . race ( [ responsePromise , responseTimeout ] ) ;
223+ await chatbotPage . sendMessage ( statusQuery ) ;
224+ await chatbotPage . waitForResponse ( statusQuery ) ;
141225
142226 // Get the last message from chatbot
143227 const messages = await page . getByTestId ( "chat-message-text" ) . all ( ) ;
0 commit comments