Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

586 řádky
20KB

  1. Option Explicit
  2. Dim fso, logDir, viewsDir, testViewsDir, pass
  3. Dim dbConnStringPath, dbConnString, dbSectionAvailable
  4. pass = True
  5. Set fso = CreateObject("Scripting.FileSystemObject")
  6. logDir = fso.GetParentFolderName(WScript.ScriptFullName) & "\test-logs"
  7. If fso.FolderExists(logDir) Then
  8. fso.DeleteFolder logDir, True
  9. End If
  10. ' Real Views/ used for the /hello integration path (proves the end-to-end
  11. ' rendering pipeline, not just the ViewRenderer component in isolation).
  12. viewsDir = fso.GetParentFolderName(WScript.ScriptFullName) & "\..\Views"
  13. ' The connection string (with live credentials) lives only in the untracked
  14. ' db.connectionstring file (see .gitignore, docs/DECISIONS.md) - never
  15. ' hardcoded here. Read up front so every RunApplication call (including the
  16. ' non-DB ones above the Database contract section below) sees a consistent
  17. ' value. If the file isn't present on this machine, dbSectionAvailable gates
  18. ' the Database contract tests as NOT RUN rather than FAIL further down.
  19. dbConnStringPath = fso.GetParentFolderName(WScript.ScriptFullName) & "\..\db.connectionstring"
  20. dbSectionAvailable = fso.FileExists(dbConnStringPath)
  21. dbConnString = ""
  22. If dbSectionAvailable Then
  23. Dim dbConnStringStreamEarly
  24. Set dbConnStringStreamEarly = fso.OpenTextFile(dbConnStringPath, 1)
  25. dbConnString = Trim(dbConnStringStreamEarly.ReadAll)
  26. dbConnStringStreamEarly.Close
  27. Set dbConnStringStreamEarly = Nothing
  28. End If
  29. ' Disposable fixture directory for direct ViewRenderer contract tests below.
  30. testViewsDir = fso.GetParentFolderName(WScript.ScriptFullName) & "\test-views"
  31. If fso.FolderExists(testViewsDir) Then
  32. fso.DeleteFolder testViewsDir, True
  33. End If
  34. fso.CreateFolder testViewsDir
  35. Function NewContext(path, httpMethod)
  36. Dim ctx
  37. Set ctx = CreateObject("WscMvc.RequestContext")
  38. ctx.Initialize path, httpMethod, logDir
  39. Set NewContext = ctx
  40. End Function
  41. Sub RunApplication(app, path, httpMethod, applicationName, statusLine, contentType, body, allowHeader)
  42. Dim ctx
  43. Set ctx = NewContext(path, httpMethod)
  44. statusLine = "" : contentType = "" : body = "" : allowHeader = ""
  45. app.Run ctx, applicationName, viewsDir, dbConnString, statusLine, contentType, body, allowHeader
  46. Set ctx = Nothing
  47. End Sub
  48. Sub WriteFixture(fileName, content)
  49. Dim stream
  50. Set stream = fso.CreateTextFile(testViewsDir & "\" & fileName, True)
  51. stream.Write content
  52. stream.Close
  53. Set stream = Nothing
  54. End Sub
  55. Sub CheckEqual(actual, expected, label)
  56. If actual = expected Then
  57. WScript.Echo "PASS: " & label
  58. Else
  59. WScript.Echo "FAIL: " & label & " -> expected [" & expected & "] got [" & actual & "]"
  60. pass = False
  61. End If
  62. End Sub
  63. ' --- RequestContext contract ---
  64. Dim ctx1
  65. On Error Resume Next
  66. Set ctx1 = NewContext("/hello", "GET")
  67. If Err.Number <> 0 Then
  68. WScript.Echo "FAIL: could not create/initialize WscMvc.RequestContext - " & Err.Description
  69. pass = False
  70. Err.Clear
  71. End If
  72. On Error Goto 0
  73. If pass Then
  74. CheckEqual ctx1.Path, "/hello", "RequestContext.Path"
  75. CheckEqual ctx1.HttpMethod, "GET", "RequestContext.HttpMethod"
  76. If Len(ctx1.CorrelationId) > 0 Then
  77. WScript.Echo "PASS: RequestContext.CorrelationId non-empty (" & ctx1.CorrelationId & ")"
  78. Else
  79. WScript.Echo "FAIL: RequestContext.CorrelationId empty"
  80. pass = False
  81. End If
  82. If ctx1.ElapsedMs() >= 0 Then
  83. WScript.Echo "PASS: RequestContext.ElapsedMs non-negative (" & ctx1.ElapsedMs() & ")"
  84. Else
  85. WScript.Echo "FAIL: RequestContext.ElapsedMs negative"
  86. pass = False
  87. End If
  88. End If
  89. ' --- Two independent contexts must not share correlation IDs or state ---
  90. Dim ctx2
  91. If pass Then
  92. Set ctx2 = NewContext("/does-not-exist", "GET")
  93. If ctx1.CorrelationId <> ctx2.CorrelationId Then
  94. WScript.Echo "PASS: two RequestContext instances have distinct CorrelationIds"
  95. Else
  96. WScript.Echo "FAIL: two RequestContext instances produced the same CorrelationId - " & ctx1.CorrelationId
  97. pass = False
  98. End If
  99. End If
  100. ' --- Application.Run happy path via ctx ---
  101. Dim app, statusLine, contentType, body, allowHeader
  102. If pass Then
  103. On Error Resume Next
  104. Set app = CreateObject("WscMvc.Application")
  105. If Err.Number <> 0 Then
  106. WScript.Echo "FAIL: could not create WscMvc.Application - " & Err.Description
  107. pass = False
  108. Err.Clear
  109. End If
  110. On Error Goto 0
  111. End If
  112. If pass Then
  113. On Error Resume Next
  114. RunApplication app, "/hello", "GET", "production", statusLine, contentType, body, allowHeader
  115. If Err.Number <> 0 Then
  116. WScript.Echo "FAIL: Application.Run raised error on /hello - " & Err.Description
  117. pass = False
  118. Err.Clear
  119. End If
  120. On Error Goto 0
  121. End If
  122. If pass Then
  123. CheckEqual statusLine, "200 OK", "Application.Run(/hello) statusLine"
  124. CheckEqual contentType, "text/html; charset=utf-8", "Application.Run(/hello) contentType"
  125. CheckEqual body, "Hello from WSC-MVC!", "Application.Run(/hello) body"
  126. CheckEqual allowHeader, "", "Application.Run(/hello) Allow header"
  127. End If
  128. ' --- Application.Run unknown route via ctx (expected 404, not an error) ---
  129. If pass Then
  130. On Error Resume Next
  131. RunApplication app, "/does-not-exist", "GET", "production", statusLine, contentType, body, allowHeader
  132. If Err.Number <> 0 Then
  133. WScript.Echo "FAIL: Application.Run raised error on unknown route - " & Err.Description
  134. pass = False
  135. Err.Clear
  136. End If
  137. On Error Goto 0
  138. End If
  139. If pass Then
  140. CheckEqual statusLine, "404 Not Found", "Application.Run(unknown route) statusLine"
  141. CheckEqual allowHeader, "", "Application.Run(unknown route) Allow header"
  142. End If
  143. ' --- Router method contract: existing route with unsupported method is 405 + Allow ---
  144. If pass Then
  145. On Error Resume Next
  146. RunApplication app, "/hello", "POST", "production", statusLine, contentType, body, allowHeader
  147. If Err.Number <> 0 Then
  148. WScript.Echo "FAIL: Application.Run raised error on method mismatch - " & Err.Description
  149. pass = False
  150. Err.Clear
  151. End If
  152. On Error Goto 0
  153. End If
  154. If pass Then
  155. CheckEqual statusLine, "405 Method Not Allowed", "Application.Run(POST /hello) statusLine"
  156. CheckEqual contentType, "text/plain; charset=utf-8", "Application.Run(POST /hello) contentType"
  157. CheckEqual body, "Method Not Allowed", "Application.Run(POST /hello) body"
  158. CheckEqual allowHeader, "GET", "Application.Run(POST /hello) Allow header"
  159. End If
  160. ' --- Router app isolation and POST support for test app route ---
  161. If pass Then
  162. On Error Resume Next
  163. RunApplication app, "/self-test", "POST", "tests", statusLine, contentType, body, allowHeader
  164. If Err.Number <> 0 Then
  165. WScript.Echo "FAIL: Application.Run raised error on POST /self-test - " & Err.Description
  166. pass = False
  167. Err.Clear
  168. End If
  169. On Error Goto 0
  170. End If
  171. If pass Then
  172. CheckEqual statusLine, "200 OK", "Application.Run(POST /self-test tests) statusLine"
  173. CheckEqual contentType, "application/json; charset=utf-8", "Application.Run(POST /self-test tests) contentType"
  174. CheckEqual allowHeader, "", "Application.Run(POST /self-test tests) Allow header"
  175. End If
  176. ' --- Router malformed path contract ---
  177. If pass Then
  178. On Error Resume Next
  179. RunApplication app, "/hello/../secret", "GET", "production", statusLine, contentType, body, allowHeader
  180. If Err.Number <> 0 Then
  181. WScript.Echo "FAIL: Application.Run raised error on malformed path - " & Err.Description
  182. pass = False
  183. Err.Clear
  184. End If
  185. On Error Goto 0
  186. End If
  187. If pass Then
  188. CheckEqual statusLine, "400 Bad Request", "Application.Run(malformed path) statusLine"
  189. CheckEqual contentType, "text/plain; charset=utf-8", "Application.Run(malformed path) contentType"
  190. CheckEqual body, "Bad Request", "Application.Run(malformed path) body"
  191. CheckEqual allowHeader, "", "Application.Run(malformed path) Allow header"
  192. End If
  193. ' --- ViewRenderer contract: substitution + HTML-encoding ---
  194. Dim renderer
  195. On Error Resume Next
  196. Set renderer = CreateObject("WscMvc.ViewRenderer")
  197. If Err.Number <> 0 Then
  198. WScript.Echo "FAIL: could not create WscMvc.ViewRenderer - " & Err.Description
  199. pass = False
  200. Err.Clear
  201. End If
  202. On Error Goto 0
  203. If pass Then
  204. WriteFixture "Sample.html", "<p>{{Name}}</p>"
  205. Dim dataSample, outSample
  206. Set dataSample = CreateObject("Scripting.Dictionary")
  207. dataSample.Add "Name", "<script>alert(1)</script>"
  208. outSample = ""
  209. On Error Resume Next
  210. renderer.Render testViewsDir, "Sample", dataSample, outSample
  211. If Err.Number <> 0 Then
  212. WScript.Echo "FAIL: ViewRenderer.Render raised error on Sample - " & Err.Description
  213. pass = False
  214. Err.Clear
  215. End If
  216. On Error Goto 0
  217. If pass Then
  218. CheckEqual outSample, "<p>&lt;script&gt;alert(1)&lt;/script&gt;</p>", "ViewRenderer HTML-encodes placeholder value"
  219. End If
  220. Set dataSample = Nothing
  221. End If
  222. ' --- ViewRenderer contract: template with no placeholders passes through unchanged, data = Nothing ---
  223. If pass Then
  224. WriteFixture "Plain.html", "Plain text, no placeholders."
  225. Dim outPlain
  226. outPlain = ""
  227. On Error Resume Next
  228. renderer.Render testViewsDir, "Plain", Nothing, outPlain
  229. If Err.Number <> 0 Then
  230. WScript.Echo "FAIL: ViewRenderer.Render raised error on Plain (data=Nothing) - " & Err.Description
  231. pass = False
  232. Err.Clear
  233. End If
  234. On Error Goto 0
  235. If pass Then
  236. CheckEqual outPlain, "Plain text, no placeholders.", "ViewRenderer passes through template with no placeholders (data=Nothing)"
  237. End If
  238. End If
  239. ' --- ViewRenderer contract: missing template file is a safe, path-free error ---
  240. If pass Then
  241. Dim outMissing, missingOk, missingDetail
  242. missingOk = False
  243. outMissing = ""
  244. On Error Resume Next
  245. renderer.Render testViewsDir, "DoesNotExist", Nothing, outMissing
  246. If Err.Number <> 0 Then
  247. missingDetail = Err.Description
  248. If InStr(1, missingDetail, testViewsDir, 1) = 0 Then
  249. missingOk = True
  250. End If
  251. Err.Clear
  252. End If
  253. On Error Goto 0
  254. If missingOk Then
  255. WScript.Echo "PASS: ViewRenderer.Render on missing template raises a path-free error"
  256. Else
  257. WScript.Echo "FAIL: ViewRenderer.Render on missing template -> Err.Number after call, detail=[" & missingDetail & "]"
  258. pass = False
  259. End If
  260. End If
  261. ' --- ViewRenderer contract: unresolved placeholder (no matching dictionary key) is a safe error ---
  262. If pass Then
  263. WriteFixture "Missing.html", "{{Unknown}}"
  264. Dim dataEmpty, outMissingKey, unresolvedOk
  265. Set dataEmpty = CreateObject("Scripting.Dictionary")
  266. unresolvedOk = False
  267. outMissingKey = ""
  268. On Error Resume Next
  269. renderer.Render testViewsDir, "Missing", dataEmpty, outMissingKey
  270. If Err.Number <> 0 Then
  271. unresolvedOk = True
  272. Err.Clear
  273. End If
  274. On Error Goto 0
  275. If unresolvedOk Then
  276. WScript.Echo "PASS: ViewRenderer.Render raises on unresolved placeholder"
  277. Else
  278. WScript.Echo "FAIL: ViewRenderer.Render did not raise on unresolved placeholder -> got [" & outMissingKey & "]"
  279. pass = False
  280. End If
  281. Set dataEmpty = Nothing
  282. End If
  283. ' --- ViewRenderer contract: invalid view name (path traversal attempt) is rejected ---
  284. If pass Then
  285. Dim outTraversal, traversalOk
  286. traversalOk = False
  287. outTraversal = ""
  288. On Error Resume Next
  289. renderer.Render testViewsDir, "../secret", Nothing, outTraversal
  290. If Err.Number <> 0 Then
  291. traversalOk = True
  292. Err.Clear
  293. End If
  294. On Error Goto 0
  295. If traversalOk Then
  296. WScript.Echo "PASS: ViewRenderer.Render rejects an invalid view name"
  297. Else
  298. WScript.Echo "FAIL: ViewRenderer.Render did not reject invalid view name -> got [" & outTraversal & "]"
  299. pass = False
  300. End If
  301. End If
  302. Set renderer = Nothing
  303. ' --- Database contract (M5) ---
  304. ' Targets the dedicated integration test database created by
  305. ' tools/Setup-TestDatabase.ps1 (never auto-created by application code -
  306. ' SPEC SS10: no broad filesystem/DB write privileges from a web request).
  307. ' dbConnString/dbSectionAvailable were already read at the top of this file
  308. ' (before the first RunApplication call). If db.connectionstring isn't
  309. ' present on this machine, the DB section is skipped (NOT RUN) rather than
  310. ' failed, since the real issue is an unprovisioned environment, not a
  311. ' framework defect.
  312. Dim db
  313. If Not dbSectionAvailable Then
  314. WScript.Echo "NOT RUN: Database contract tests skipped - db.connectionstring not found at " & dbConnStringPath
  315. End If
  316. If pass And dbSectionAvailable Then
  317. On Error Resume Next
  318. Set db = CreateObject("WscMvc.Database")
  319. If Err.Number <> 0 Then
  320. WScript.Echo "FAIL: could not create WscMvc.Database - " & Err.Description
  321. pass = False
  322. Err.Clear
  323. End If
  324. On Error Goto 0
  325. End If
  326. If pass And dbSectionAvailable Then
  327. On Error Resume Next
  328. db.Open dbConnString
  329. If Err.Number <> 0 Then
  330. WScript.Echo "FAIL: Database.Open raised error - " & Err.Description
  331. pass = False
  332. Err.Clear
  333. End If
  334. On Error Goto 0
  335. End If
  336. ' Clean slate so this test is deterministic across repeated runs.
  337. If pass And dbSectionAvailable Then
  338. Dim rowsCleared
  339. On Error Resume Next
  340. db.ExecuteNonQuery "DELETE FROM dbo.Widgets", "", Nothing, rowsCleared
  341. If Err.Number <> 0 Then
  342. WScript.Echo "FAIL: Database.ExecuteNonQuery(DELETE) raised error - " & Err.Description
  343. pass = False
  344. Err.Clear
  345. End If
  346. On Error Goto 0
  347. End If
  348. ' --- Parameterized INSERT with a value containing a single quote: proves no
  349. ' string-concatenation SQL injection (a naive concatenation would break the
  350. ' SQL syntax or need manual quote-escaping; the parameterized call needs
  351. ' neither). ---
  352. If pass And dbSectionAvailable Then
  353. Dim insertParams, rowsInserted
  354. Set insertParams = CreateObject("Scripting.Dictionary")
  355. insertParams.Add "Name", "O'Brien"
  356. On Error Resume Next
  357. db.ExecuteNonQuery "INSERT INTO dbo.Widgets (Name) VALUES (?)", "Name", insertParams, rowsInserted
  358. If Err.Number <> 0 Then
  359. WScript.Echo "FAIL: Database.ExecuteNonQuery(INSERT) raised error - " & Err.Description
  360. pass = False
  361. Err.Clear
  362. End If
  363. On Error Goto 0
  364. If pass Then
  365. CheckEqual rowsInserted, 1, "Database.ExecuteNonQuery(INSERT) rowsAffected"
  366. End If
  367. Set insertParams = Nothing
  368. End If
  369. ' --- Parameterized SELECT round-trips the quote-containing value intact ---
  370. If pass And dbSectionAvailable Then
  371. Dim selectParams, foundName
  372. Set selectParams = CreateObject("Scripting.Dictionary")
  373. selectParams.Add "Name", "O'Brien"
  374. On Error Resume Next
  375. db.ExecuteScalar "SELECT Name FROM dbo.Widgets WHERE Name = ?", "Name", selectParams, foundName
  376. If Err.Number <> 0 Then
  377. WScript.Echo "FAIL: Database.ExecuteScalar(SELECT) raised error - " & Err.Description
  378. pass = False
  379. Err.Clear
  380. End If
  381. On Error Goto 0
  382. If pass Then
  383. CheckEqual foundName, "O'Brien", "Database.ExecuteScalar round-trips quote-containing value"
  384. End If
  385. Set selectParams = Nothing
  386. End If
  387. ' --- Null parameter contract: a Null value must pass through as a real SQL
  388. ' NULL, not the string "Null" or an empty string. ---
  389. If pass And dbSectionAvailable Then
  390. Dim nullParams, nullResult
  391. Set nullParams = CreateObject("Scripting.Dictionary")
  392. nullParams.Add "v", Null
  393. On Error Resume Next
  394. db.ExecuteScalar "SELECT ? AS v", "v", nullParams, nullResult
  395. If Err.Number <> 0 Then
  396. WScript.Echo "FAIL: Database.ExecuteScalar(Null param) raised error - " & Err.Description
  397. pass = False
  398. Err.Clear
  399. End If
  400. On Error Goto 0
  401. If pass Then
  402. If IsNull(nullResult) Then
  403. WScript.Echo "PASS: Database.ExecuteScalar Null parameter round-trips as SQL NULL"
  404. Else
  405. WScript.Echo "FAIL: Database.ExecuteScalar Null parameter -> expected Null, got [" & nullResult & "]"
  406. pass = False
  407. End If
  408. End If
  409. Set nullParams = Nothing
  410. End If
  411. ' --- Transaction contract: rollback leaves no trace ---
  412. If pass And dbSectionAvailable Then
  413. Dim rollbackParams, rowsRolledBack, countAfterRollback
  414. On Error Resume Next
  415. db.BeginTransaction
  416. Set rollbackParams = CreateObject("Scripting.Dictionary")
  417. rollbackParams.Add "Name", "RolledBack"
  418. db.ExecuteNonQuery "INSERT INTO dbo.Widgets (Name) VALUES (?)", "Name", rollbackParams, rowsRolledBack
  419. db.RollbackTransaction
  420. If Err.Number <> 0 Then
  421. WScript.Echo "FAIL: Database rollback sequence raised error - " & Err.Description
  422. pass = False
  423. Err.Clear
  424. End If
  425. On Error Goto 0
  426. If pass Then
  427. Dim countParams
  428. Set countParams = CreateObject("Scripting.Dictionary")
  429. countParams.Add "Name", "RolledBack"
  430. On Error Resume Next
  431. db.ExecuteScalar "SELECT COUNT(*) FROM dbo.Widgets WHERE Name = ?", "Name", countParams, countAfterRollback
  432. On Error Goto 0
  433. CheckEqual countAfterRollback, 0, "Database.RollbackTransaction leaves no row"
  434. Set countParams = Nothing
  435. End If
  436. Set rollbackParams = Nothing
  437. End If
  438. ' --- Transaction contract: commit persists the change ---
  439. If pass And dbSectionAvailable Then
  440. Dim commitParams, rowsCommitted, countAfterCommit
  441. On Error Resume Next
  442. db.BeginTransaction
  443. Set commitParams = CreateObject("Scripting.Dictionary")
  444. commitParams.Add "Name", "Committed"
  445. db.ExecuteNonQuery "INSERT INTO dbo.Widgets (Name) VALUES (?)", "Name", commitParams, rowsCommitted
  446. db.CommitTransaction
  447. If Err.Number <> 0 Then
  448. WScript.Echo "FAIL: Database commit sequence raised error - " & Err.Description
  449. pass = False
  450. Err.Clear
  451. End If
  452. On Error Goto 0
  453. If pass Then
  454. On Error Resume Next
  455. db.ExecuteScalar "SELECT COUNT(*) FROM dbo.Widgets WHERE Name = ?", "Name", commitParams, countAfterCommit
  456. On Error Goto 0
  457. CheckEqual countAfterCommit, 1, "Database.CommitTransaction persists the row"
  458. End If
  459. Set commitParams = Nothing
  460. End If
  461. ' --- Failure path: operating on a closed/never-opened connection is a safe error ---
  462. If pass And dbSectionAvailable Then
  463. Dim db2, closedOk, closedDetail, dummyResult
  464. closedOk = False
  465. Set db2 = CreateObject("WscMvc.Database")
  466. On Error Resume Next
  467. db2.ExecuteScalar "SELECT 1", "", Nothing, dummyResult
  468. If Err.Number <> 0 Then
  469. closedOk = True
  470. Err.Clear
  471. End If
  472. On Error Goto 0
  473. If closedOk Then
  474. WScript.Echo "PASS: Database.ExecuteScalar on an unopened connection raises a safe error"
  475. Else
  476. WScript.Echo "FAIL: Database.ExecuteScalar on an unopened connection did not raise -> got [" & dummyResult & "]"
  477. pass = False
  478. End If
  479. Set db2 = Nothing
  480. End If
  481. ' --- Close is idempotent ---
  482. If pass And dbSectionAvailable Then
  483. Dim closeOk
  484. closeOk = True
  485. On Error Resume Next
  486. db.Close
  487. db.Close
  488. If Err.Number <> 0 Then
  489. closeOk = False
  490. Err.Clear
  491. End If
  492. On Error Goto 0
  493. If closeOk Then
  494. WScript.Echo "PASS: Database.Close is idempotent"
  495. Else
  496. WScript.Echo "FAIL: calling Database.Close twice raised an error"
  497. pass = False
  498. End If
  499. End If
  500. Set db = Nothing
  501. ' --- Logging: best-effort log file was written with both outcomes ---
  502. If pass Then
  503. Dim logPath, logContent
  504. logPath = logDir & "\app.log"
  505. If fso.FileExists(logPath) Then
  506. Dim logStream
  507. Set logStream = fso.OpenTextFile(logPath, 1)
  508. logContent = logStream.ReadAll
  509. logStream.Close
  510. If InStr(logContent, "200 OK") > 0 And InStr(logContent, "404 Not Found") > 0 Then
  511. WScript.Echo "PASS: app.log contains both 200 OK and 404 Not Found outcomes"
  512. Else
  513. WScript.Echo "FAIL: app.log missing expected outcomes -> " & logContent
  514. pass = False
  515. End If
  516. Else
  517. WScript.Echo "FAIL: app.log was not created at " & logPath
  518. pass = False
  519. End If
  520. End If
  521. Set app = Nothing
  522. Set ctx1 = Nothing
  523. Set ctx2 = Nothing
  524. If fso.FolderExists(logDir) Then
  525. fso.DeleteFolder logDir, True
  526. End If
  527. If fso.FolderExists(testViewsDir) Then
  528. fso.DeleteFolder testViewsDir, True
  529. End If
  530. Set fso = Nothing
  531. If pass Then
  532. WScript.Echo "RESULT: ALL PASS"
  533. WScript.Quit 0
  534. Else
  535. WScript.Echo "RESULT: FAILURE"
  536. WScript.Quit 1
  537. End If

Powered by TurnKey Linux.