From 5a585e043ac9a9d14e5d6b63ab510c78af7f2bae Mon Sep 17 00:00:00 2001 From: Ricardo Junior Date: Tue, 15 Sep 2026 14:35:48 -0300 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20implementado=20testes=20unit=C3=A1r?= =?UTF-8?q?ios=20com=20base=20de=20dados=20e=20sem=20base=20de=20dados,=20?= =?UTF-8?q?implementado=20regerador=20de=20bases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 35 +- src/ccs/sourcecontrol/client.ts | 8 + src/ccs/sourcecontrol/routes.ts | 13 + src/commands/unitTest.ts | 1117 +++++++++++++++++++++++++++++-- src/extension.ts | 6 +- 5 files changed, 1118 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index 9c68ceee..06cd57a9 100644 --- a/package.json +++ b/package.json @@ -775,6 +775,11 @@ "command": "vscode-objectscript.ccs.atualizarConfiguracoes", "group": "7_config@1", "when": "vscode-objectscript.connectActive" + }, + { + "command": "vscode-objectscript.ccs.gerenciarBasesTeste", + "group": "8_bases@1", + "when": "vscode-objectscript.connectActive" } ] }, @@ -1097,6 +1102,12 @@ "category": "ObjectScript", "icon": "$(plug)" }, + { + "command": "vscode-objectscript.ccs.gerenciarBasesTeste", + "title": "Regerar Base de Testes", + "category": "Consistem", + "icon": "$(database)" + }, { "category": "ObjectScript", "command": "vscode-objectscript.jumpToTagAndOffset", @@ -1826,12 +1837,32 @@ "scope": "resource" }, "objectscript.unitTest.legacyRequestTimeout": { - "description": "Timeout in milliseconds applied to legacy unit test API calls. Set to 0 for no timeout.", + "description": "Timeout in milliseconds applied to each unit test class execution. Set to 0 for no timeout.", "type": "integer", - "default": 6000000, + "default": 600000, "minimum": 0, "scope": "resource" }, + "objectscript.unitTest.usarExecutorAntigo": { + "description": "Volta ao executor de testes anterior (sempre executa no namespace TESTEUNITARIO). Temporário, apenas para rollback durante a validação do executor novo.", + "type": "boolean", + "default": false, + "scope": "resource" + }, + "objectscript.unitTest.incluirAsserts": { + "description": "Quais asserts o executor de testes deve retornar. 'somenteFalhas' reduz muito o volume de dados em classes que passam.", + "type": "string", + "enum": [ + "somenteFalhas", + "todos" + ], + "enumDescriptions": [ + "Apenas os asserts que falharam, mais as mensagens de log", + "Todos os asserts, inclusive os que passaram" + ], + "default": "somenteFalhas", + "scope": "resource" + }, "objectscript.commentToken": { "description": "The line comment characters for ObjectScript in classes and MAC and INC routines.", "type": "string", diff --git a/src/ccs/sourcecontrol/client.ts b/src/ccs/sourcecontrol/client.ts index 265bc819..fbd6614d 100644 --- a/src/ccs/sourcecontrol/client.ts +++ b/src/ccs/sourcecontrol/client.ts @@ -85,4 +85,12 @@ export class SourceControlApi { ): Promise> { return this.client.post(route, data, config); } + + public get(route: string, config?: AxiosRequestConfig): Promise> { + return this.client.get(route, config); + } + + public delete(route: string, config?: AxiosRequestConfig): Promise> { + return this.client.delete(route, config); + } } diff --git a/src/ccs/sourcecontrol/routes.ts b/src/ccs/sourcecontrol/routes.ts index 98af8deb..20be8297 100644 --- a/src/ccs/sourcecontrol/routes.ts +++ b/src/ccs/sourcecontrol/routes.ts @@ -13,6 +13,19 @@ export const ROUTES = { `/namespaces/${encodeURIComponent(namespace)}/converterArquivoCustomizado`, analizarVersaoItem: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/analizarVersaoItem`, atualizarConfig: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/atualizarConfig`, + resolveUnitTests: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/unitTests/resolve`, + executarClasseTeste: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/unitTests/executarClasse`, + situacaoExecucaoTeste: (namespace: string, idExecucao: string) => + `/namespaces/${encodeURIComponent(namespace)}/unitTests/execucao/${encodeURIComponent(idExecucao)}`, + paginaResultadoTeste: (namespace: string, idExecucao: string) => + `/namespaces/${encodeURIComponent(namespace)}/unitTests/resultado/${encodeURIComponent(idExecucao)}`, + gerarBaseTeste: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/unitTests/gerarBase`, + listarBasesTeste: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/unitTests/bases`, + regerarBaseTeste: (namespace: string) => `/namespaces/${encodeURIComponent(namespace)}/unitTests/bases/regerar`, + prepararAmbienteTeste: (namespace: string) => + `/namespaces/${encodeURIComponent(namespace)}/unitTests/prepararAmbiente`, + situacaoGeracaoBase: (namespace: string, idGeracao: string) => + `/namespaces/${encodeURIComponent(namespace)}/unitTests/gerarBase/${encodeURIComponent(idGeracao)}`, } as const; export type RouteKey = keyof typeof ROUTES; diff --git a/src/commands/unitTest.ts b/src/commands/unitTest.ts index 570b8eed..4f9ec9a8 100644 --- a/src/commands/unitTest.ts +++ b/src/commands/unitTest.ts @@ -4,6 +4,7 @@ import * as vscode from "vscode"; import * as Atelier from "../api/atelier"; import { clsLangId, extensionId, filesystemSchemas, lsExtensionId, sendUnitTestTelemetryEvent } from "../extension"; import { + currentWorkspaceFolder, getFileText, handleError, methodOffsetToLine, @@ -74,6 +75,143 @@ interface LegacyUnitTestResponse { console?: string[]; } +// --------------------------------------------------------------------------------------- +// API v2 do executor Consistem: uma classe por requisicao, resultado paginado. +// +// Substitui LegacyUnitTestResponse. Todos os campos vem prontos do backend, que le direto o +// ^UnitTest.Result do IRIS -- nao ha mais nada a derivar no cliente. +// --------------------------------------------------------------------------------------- + +/** Situacao de um grupo de classes no plano de execucao */ +type SituacaoGrupo = "pronta" | "baseAusente" | "baseAmbigua" | "baseNaoGeravel" | "ambienteAusente"; + +interface BaseDados { + id: string; + tipo: number; + tipoDescricao: string; + /** Versão do ERP para a qual a base foi publicada */ + versao?: string; + /** A base é de uma versão maior que a desta instalação. Aviso, não impedimento. */ + versaoIncompativel?: boolean; +} + +interface GrupoExecucao { + /** `null` quando as classes do grupo nao usam base de dados */ + base: BaseDados | null; + namespace: string; + namespacesCandidatos: string[]; + situacao: SituacaoGrupo; + podeGerar: boolean; + classes: string[]; +} + +interface ClasseNaoExecutavel { + classe: string; + motivo: string; + mensagem: string; +} + +interface ResolucaoResponse { + versao: number; + namespaceSolicitado: string; + grupos: GrupoExecucao[]; + naoExecutaveis: ClasseNaoExecutavel[]; +} + +interface LocalAssert { + documento: string; + texto: string; + label?: string; + offset?: number; + namespace?: string; +} + +interface ResultadoAssert { + sequencia: number; + status: TestStatus; + /** `log` sao os LogMessage do %UnitTest; `assert` e o resto */ + nivel: "assert" | "log"; + tipo: string; + mensagem: string; + /** + * `null` nos asserts customizados da Consistem (AssertEqualsTable, AssertEqualsGlobal e + * afins), que chamam LogAssert sem location. A mensagem TEM de aparecer mesmo assim. + */ + local: LocalAssert | null; +} + +interface ResultadoMetodo { + /** Rotulo sem o prefixo `Test` -- casa com o label do TestItem */ + metodo: string; + metodoCompleto: string; + status: TestStatus; + duracaoMs: number; + erroAcao: string; + erro: string; + assertsPassaram: number; + /** `true` quando os asserts deste metodo continuam na proxima pagina */ + parcial: boolean; + asserts: ResultadoAssert[]; +} + +interface Diagnostico { + nivel: string; + codigo: string; + mensagem: string; +} + +interface Paginacao { + idExecucao: string; + incluirAsserts: string; + fim: boolean; + cursor: string | null; +} + +interface ResultadoClasse { + versao: number; + execucao: { + classe: string; + suite?: string; + nomeClasse?: string; + namespaceExec: string; + idLog?: number; + status: TestStatus; + duracaoMs: number; + erroAcao?: string; + erro?: string; + totais?: { + metodos: number; + passou: number; + falhou: number; + ignorado: number; + asserts: number; + assertsFalharam: number; + }; + }; + metodos: ResultadoMetodo[]; + paginacao?: Paginacao; + diagnosticos?: Diagnostico[]; +} + +interface SituacaoExecucao { + versao: number; + idExecucao: string; + situacao: "executando" | "concluida" | "erro"; + codigo: string; + mensagem: string; + duracaoMs: number; +} + +interface SituacaoGeracao { + versao: number; + idGeracao: string; + idBase: string; + situacao: "executando" | "concluida" | "erro"; + namespace: string; + mensagem: string; + duracaoMs: number; +} + interface DerivedMethodSummary { status?: TestStatus; failures: { message: string; location?: TestAssertLocation }[]; @@ -105,6 +243,17 @@ const GLOB_PATTERN = /[*?]/; const DEFAULT_LEGACY_REQUEST_TIMEOUT = 10 * 60 * 1000; // 10 minutes +/** Timeout da consulta de situacao. O default do cliente (5s) e curto demais com a maquina + * ocupada rodando o teste. */ +const POLL_TIMEOUT = 30 * 1000; + +/** Falhas de transporte seguidas toleradas antes de desistir de acompanhar uma execucao */ +const POLL_MAX_FALHAS = 10; + +const POLL_INTERVALO_INICIAL = 500; + +const POLL_INTERVALO_MAXIMO = 5000; + const testResultDecorationType = vscode.window.createTextEditorDecorationType({ after: { margin: "0 0 0 3ch", @@ -264,7 +413,7 @@ function extractMethodDurations(classResult: TestResult): Map { } function applyTestResultDecorations( - methodResults: { item: vscode.TestItem; result: TestResult; durationText?: string }[] + methodResults: { item: vscode.TestItem; status: TestStatus; durationText?: string }[] ): void { if (!methodResults.length) { return; @@ -276,7 +425,7 @@ function applyTestResultDecorations( decorationsByEditor.set(editor, []); } - for (const { item, result, durationText } of methodResults) { + for (const { item, status, durationText } of methodResults) { const editor = vscode.window.visibleTextEditors.find( (e) => item.uri && e.document.uri.toString() === item.uri.toString() ); @@ -287,8 +436,7 @@ function applyTestResultDecorations( const line = item.range.start.line; const lineRange = editor.document.lineAt(line).range; - const statusText = - result.status === TestStatus.Passed ? "passed" : result.status === TestStatus.Failed ? "failed" : "skipped"; + const statusText = status === TestStatus.Passed ? "passed" : status === TestStatus.Failed ? "failed" : "skipped"; const parts: string[] = [`${item.label} ${statusText}`]; if (durationText) { @@ -415,12 +563,41 @@ function labelFromAssertion(assertion: LegacyAssertion): string | undefined { return assertion.location?.label ?? parseLegacyLabelFromText(assertion.locationText); } +/** + * URI dos itens de DIRETORIO, que sao criados sem `uri`. Ver `itemUri()`. + * + * Guarda o objeto `vscode.Uri` original em vez de reconstrui-lo a partir do `id`: no Windows, + * `Uri.toString()` minusculiza a letra do drive (`/C:/...` -> `/c:/...`), entao um `Uri.parse(id)` + * devolveria um path diferente do de `folder.uri` e quebraria as comparacoes de `uriIsAncestorOf`, + * que sao case-sensitive. + */ +const uriForDirectoryItem: WeakMap = new WeakMap(); + +/** + * URI que o `TestItem` representa. + * + * Itens de DIRETORIO (raiz do workspace e subpastas/pacotes) sao criados SEM `uri` de proposito. + * O VS Code, ao clicar no rotulo de um item da view Testing, executa `vscode.revealTest` quando o + * item ainda nao tem filhos resolvidos e tem `uri` + * (`!e.element.children.size && e.element.test.item.uri` em `testingExplorerView.ts`). + * Com `uri` de pasta, esse reveal cai no Explorer -- e so na PRIMEIRA vez, porque depois de expandida + * a pasta ja tem filhos e a condicao deixa de valer. Sem `uri`, o clique so expande/colapsa na Testing. + * + * Itens de classe (.cls) e de metodo mantem `uri` (+ `range`), que e o que permite abrir o fonte. + */ +function itemUri(item: vscode.TestItem): vscode.Uri { + // O parse do `id` e so uma rede de seguranca: todo item de diretorio entra em `uriForDirectoryItem` + // quando e criado, e o `id` e o `uri.toString()` do proprio item. + return item.uri ?? uriForDirectoryItem.get(item) ?? vscode.Uri.parse(item.id); +} + /** Find the root `TestItem` for `uri` */ function rootItemForItem(testController: vscode.TestController, uri: vscode.Uri): vscode.TestItem | undefined { let rootItem: vscode.TestItem | undefined; const uriString = uri.toString(); for (const [, i] of testController.items) { - if (uriIsAncestorOf(i.uri!, uri) || uriString == i.uri!.toString()) { + const rootUri = itemUri(i); + if (uriIsAncestorOf(rootUri, uri) || uriString == rootUri.toString()) { rootItem = i; break; } @@ -653,7 +830,9 @@ function createRootItemsForWorkspaceFolder( : undefined; const rootUri = folder.uri; - const rootItem = testController.createTestItem(rootUri.toString(), folder.name, rootUri); + // Created without a `uri` on purpose: it's a directory, not a file (see `itemUri()`) + const rootItem = testController.createTestItem(rootUri.toString(), folder.name); + uriForDirectoryItem.set(rootItem, rootUri); if (notIsfs(folder.uri)) { const roots = relativeTestRootsForUri(folder.uri); @@ -718,12 +897,15 @@ async function getTestItemForClass( const rootItem = rootItemForItem(testController, uri); if (rootItem && !rootItem.error) { // Walk the directory path until we reach a dead end or the TestItem for this class - let docPath = uri.path.slice(rootItem.uri!.path.length); + let docPath = uri.path.slice(itemUri(rootItem).path.length); docPath = docPath.startsWith("/") ? docPath.slice(1) : docPath; const docPathParts = docPath.split("/"); item = rootItem; for (const part of docPathParts) { - const currUri = item.uri!.with({ path: `${item.uri!.path}${!item.uri!.path.endsWith("/") ? "/" : ""}${part}` }); + const parentUri = itemUri(item); + const currUri = parentUri.with({ + path: `${parentUri.path}${!parentUri.path.endsWith("/") ? "/" : ""}${part}`, + }); let currItem = item.children.get(currUri.toString()); if (!currItem && create) { // We're allowed to create non-existent directory TestItems as we walk the path @@ -755,10 +937,11 @@ function replaceRootTestItems(testController: vscode.TestController): void { async function childrenForServerSideFolderItem( item: vscode.TestItem ): Promise>> { - const { project, system, generated, mapped } = isfsConfig(item.uri!); + const uri = itemUri(item); + const { project, system, generated, mapped } = isfsConfig(uri); let query: string; let parameters: string[]; - let folder = !item.uri!.path.endsWith("/") ? item.uri!.path + "/" : item.uri!.path; + let folder = !uri.path.endsWith("/") ? uri.path + "/" : uri.path; folder = folder.startsWith("/") ? folder.slice(1) : folder; if (folder == "/") { // Treat this the same as an empty folder @@ -766,7 +949,7 @@ async function childrenForServerSideFolderItem( } folder = folder.replace(/\//g, "."); const folderLen = String(folder.length + 1); // Need the + 1 because SUBSTR is 1 indexed - const api = new AtelierAPI(item.uri!); + const api = new AtelierAPI(uri); if (project) { query = "SELECT DISTINCT CASE " + @@ -790,7 +973,7 @@ async function childrenForServerSideFolderItem( folderLen, folderLen, folderLen, - fileSpecFromURI(item.uri!), + fileSpecFromURI(uri), "1", "1", system ? "1" : "0", @@ -808,12 +991,16 @@ async function childrenForServerSideFolderItem( /** Create a child `TestItem` of `item` with label `child`. */ function addChildItem(testController: vscode.TestController, item: vscode.TestItem, child: string): void { - const newUri = item.uri!.with({ - path: `${item.uri!.path}${!item.uri!.path.endsWith("/") ? "/" : ""}${child}`, + const parentUri = itemUri(item); + const newUri = parentUri.with({ + path: `${parentUri.path}${!parentUri.path.endsWith("/") ? "/" : ""}${child}`, }); if (!item.children.get(newUri.toString())) { // Only add the item if it doesn't already exist - const newItem = testController.createTestItem(newUri.toString(), child, newUri); + // Directory items are created without a `uri` on purpose (see `itemUri()`) + const isClass = child.toLowerCase().endsWith(".cls"); + const newItem = testController.createTestItem(newUri.toString(), child, isClass ? newUri : undefined); + if (!isClass) uriForDirectoryItem.set(newItem, newUri); newItem.canResolveChildren = true; item.children.add(newItem); } @@ -967,12 +1154,12 @@ async function executeLegacyRunner( action: string, showOutput: boolean ): Promise { - const generateBase = await promptGenerateLegacyBase(root.uri!); + const generateBase = await promptGenerateLegacyBase(itemUri(root)); if (generateBase === undefined) { return true; } - const unitTestConfiguration = vscode.workspace.getConfiguration("objectscript.unitTest", root.uri!); + const unitTestConfiguration = vscode.workspace.getConfiguration("objectscript.unitTest", itemUri(root)); const configuredLegacyTimeout = unitTestConfiguration.get("legacyRequestTimeout"); const legacyRequestTimeout = Number.isFinite(configuredLegacyTimeout) ? Math.max(0, Math.floor(configuredLegacyTimeout!)) @@ -1058,7 +1245,7 @@ async function executeLegacyRunner( const knownStatuses: WeakMap = new WeakMap(); const classes = classesForRoot.get(root) ?? new Map(); - const workspaceFolder = vscode.workspace.getWorkspaceFolder(root.uri!); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(itemUri(root)); const documentSymbols: Map = new Map(); const filesText: Map = new Map(); const requestedMethodsByClass: Map | undefined> = new Map(); @@ -1301,7 +1488,13 @@ async function executeLegacyRunner( } } } - applyTestResultDecorations(allMethodResultsForDecorations); + applyTestResultDecorations( + allMethodResultsForDecorations.map((d) => ({ + item: d.item, + status: d.result.status, + durationText: d.durationText, + })) + ); uniqueClassItems.forEach((classItem) => { if (!knownStatuses.has(classItem)) { knownStatuses.set(classItem, TestStatus.Skipped); @@ -1322,6 +1515,670 @@ async function executeLegacyRunner( } /** The `runHandler` function for the `TestRunProfile`s. */ +/** + * Resolve a posicao no fonte de um assert que trouxe `local`. + * + * Os asserts customizados da Consistem vem com `local: null` -- nesses casos nao ha posicao, + * mas a mensagem continua tendo de aparecer. + */ +async function resolverLocalAssert( + local: LocalAssert, + classes: Map, + workspaceFolder: vscode.WorkspaceFolder | undefined, + documentSymbols: Map, + filesText: Map +): Promise { + if (!workspaceFolder) return undefined; + + const documento = local.documento; + if (!documento) return undefined; + + let locationUri: vscode.Uri | null | undefined; + const semExtensao = documento.toLowerCase().endsWith(".cls") ? documento.slice(0, -4) : undefined; + + if (semExtensao && classes.has(semExtensao)) { + locationUri = classes.get(semExtensao)!.uri; + } else { + locationUri = DocumentContentProvider.getUri(documento, workspaceFolder.name, local.namespace); + } + if (!locationUri) return undefined; + + const chave = locationUri.toString(); + + if (!documentSymbols.has(chave)) { + const novos = await vscode.commands + .executeCommand("vscode.executeDocumentSymbolProvider", locationUri) + .then( + (r) => r[0]?.children, + () => undefined + ); + if (novos != undefined) documentSymbols.set(chave, novos); + } + const simbolos = documentSymbols.get(chave); + if (simbolos == undefined) return undefined; + + if (!filesText.has(chave)) { + const texto = await getFileText(locationUri).catch(() => undefined); + if (texto != undefined) filesText.set(chave, texto); + } + const texto = filesText.get(chave); + if (texto == undefined) return undefined; + + const linha = methodOffsetToLine(simbolos, texto, local.label ?? "", local.offset ?? 0); + if (linha == undefined) return undefined; + + return new vscode.Location(locationUri, new vscode.Range(linha - 1, 0, linha, 0)); +} + +/** Uma linha de output por assert, no mesmo espirito do que o terminal imprime */ +function formatarAssert(assert: ResultadoAssert): string { + if (assert.nivel === "log") { + return ` ${assert.mensagem}\r\n`; + } + const cor = assert.status === TestStatus.Passed ? ANSI_GREEN : ANSI_RED; + const situacao = assert.status === TestStatus.Passed ? "passed" : "failed"; + return ` ${assert.tipo} - ${assert.mensagem} >> ${cor}${situacao}${ANSI_RESET}\r\n`; +} + +/** + * Aguarda o fim de uma execucao iniciada no servidor. + * + * A execucao roda em JOB porque uma classe lenta estoura o timeout do gateway CSP: medimos + * 70s numa classe de producao contra o limite padrao de 60s. Aqui so se acompanha. + */ +async function aguardarExecucao( + sourceControlApi: SourceControlApi, + namespaceReq: string, + idExecucao: string, + token: vscode.CancellationToken, + signal: AbortSignal, + timeout: number +): Promise { + const inicio = Date.now(); + + const estado = { falhas: 0 }; + let intervalo = POLL_INTERVALO_INICIAL; + + for (;;) { + if (token.isCancellationRequested) return undefined; + + // Falha de transporte é transitória: desistir aqui abandonaria um JOB que continua + // rodando e segurando o lock da base, derrubando as classes seguintes em cascata. + const situacao = await consultarSituacao( + sourceControlApi, + ROUTES.situacaoExecucaoTeste(namespaceReq, idExecucao), + signal, + estado + ); + + if (token.isCancellationRequested) return undefined; + + if (situacao?.situacao === "concluida") return situacao; + if (situacao?.situacao === "erro") { + throw new Error(situacao.mensagem || "Falha na execução do teste."); + } + + if (timeout > 0 && Date.now() - inicio > timeout) { + throw new Error(`A execução excedeu ${Math.round(timeout / 1000)}s e foi abandonada pelo cliente.`); + } + + await new Promise((resolve) => setTimeout(resolve, intervalo)); + intervalo = Math.min(Math.round(intervalo * 1.5), POLL_INTERVALO_MAXIMO); + } +} + +/** Percorre todas as paginas de um resultado e devolve os metodos acumulados */ +async function coletarPaginas( + sourceControlApi: SourceControlApi, + namespace: string, + primeira: ResultadoClasse, + incluirAsserts: string, + signal: AbortSignal, + timeout: number +): Promise { + const acumulado: ResultadoClasse = { ...primeira, metodos: [...primeira.metodos] }; + + let paginacao = primeira.paginacao; + + while (paginacao && !paginacao.fim && paginacao.cursor) { + const resposta = await sourceControlApi.get( + ROUTES.paginaResultadoTeste(namespace, paginacao.idExecucao), + { params: { cursor: paginacao.cursor, incluirAsserts }, signal, timeout } + ); + + const pagina = resposta.data; + if (!pagina || !Array.isArray(pagina.metodos)) break; + + for (const metodo of pagina.metodos) { + // Um metodo partido entre paginas volta com o mesmo nome: junta os asserts + const anterior = acumulado.metodos.find((m) => m.metodoCompleto === metodo.metodoCompleto); + if (anterior) { + anterior.asserts.push(...metodo.asserts); + anterior.parcial = metodo.parcial; + } else { + acumulado.metodos.push(metodo); + } + } + + paginacao = pagina.paginacao; + } + + acumulado.paginacao = paginacao; + return acumulado; +} + +/** + * O backend responde erro como `{ error: "..." }` com HTTP 200. + * Sem isto, qualquer falha do servidor virava "payload inválido" e a causa real sumia. + */ +function erroDoBackend(data: unknown): string | undefined { + if (typeof data === "string") { + return data.trim() ? `Resposta inesperada do servidor: ${data.slice(0, 400)}` : undefined; + } + if (data && typeof data === "object") { + const erro = (data as { error?: unknown }).error; + if (typeof erro === "string" && erro.trim()) return erro; + } + return undefined; +} + +/** + * GET de consulta de situação, com timeout explícito e tolerância a falha transitória. + * + * O default do cliente é 5s, e a máquina fica saturada rodando o próprio teste (ou criando a + * database do TESTEUNITARIO). Desistir na primeira falha abandonaria um JOB que continua + * rodando no servidor -- foi assim que uma consulta lenta derrubou a operação inteira. + * + * Devolve `undefined` numa falha tolerada; só relança depois de POLL_MAX_FALHAS seguidas. + */ +async function consultarSituacao( + sourceControlApi: SourceControlApi, + rota: string, + signal: AbortSignal | undefined, + estado: { falhas: number } +): Promise { + try { + const resposta = await sourceControlApi.get(rota, { signal, timeout: POLL_TIMEOUT }); + estado.falhas = 0; + return resposta.data; + } catch (error) { + estado.falhas += 1; + if (estado.falhas > POLL_MAX_FALHAS) throw error; + return undefined; + } +} + +/** Pergunta ao usuario se quer gerar a base que falta para um grupo de classes */ +async function perguntarGerarBase(grupo: GrupoExecucao): Promise { + const idBase = grupo.base?.id ?? ""; + const incompativel = grupo.base?.versaoIncompativel === true; + const versao = grupo.base?.versao; + + // Versão incompatível não impede gerar -- o Gerenciador de Bases do terminal apenas pede + // confirmação, com "N" como default. Aqui o aviso vai no detalhe e "Pular" vem primeiro. + const opcaoGerar = { + label: `Gerar a base ${idBase}`, + gerar: true, + detail: incompativel + ? `⚠ Base publicada para a versão ${versao}, maior que a desta instalação. Gerar mesmo assim?` + : `Necessária por ${grupo.classes.length} classe(s) de teste`, + }; + const opcaoPular = { + label: "Pular estas classes", + gerar: false, + detail: "Os testes que dependem desta base ficarão ignorados", + }; + + const escolha = await vscode.window.showQuickPick( + incompativel ? [opcaoPular, opcaoGerar] : [opcaoGerar, opcaoPular], + { + title: "Execução de testes unitários", + placeHolder: incompativel + ? `A base ${idBase} (versão ${versao}) não está montada e é de versão incompatível.` + : `A base ${idBase} não está montada nesta instalação. Gerar agora?`, + ignoreFocusOut: true, + } + ); + return escolha?.gerar === true; +} + +/** Deixa o usuario escolher o namespace quando mais de um atende a mesma base */ +async function perguntarNamespace(grupo: GrupoExecucao): Promise { + return vscode.window.showQuickPick(grupo.namespacesCandidatos, { + title: "Execução de testes unitários", + placeHolder: `Mais de um namespace atende a base ${grupo.base?.id ?? ""}. Escolha onde executar.`, + ignoreFocusOut: true, + }); +} + +/** + * Dispara uma operacao longa no servidor e acompanha ate concluir. + * + * Serve tanto para gerar uma base versionada quanto para montar o namespace TESTEUNITARIO. + * As duas dropam/criam database e namespace: dentro da requisicao HTTP estouram o gateway. + */ +async function aguardarOperacaoServidor( + sourceControlApi: SourceControlApi, + namespaceReq: string, + titulo: string, + rota: string, + corpo: unknown, + token: vscode.CancellationToken, + signal?: AbortSignal +): Promise { + return vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: titulo, + cancellable: false, + }, + async (progress) => { + progress.report({ message: "iniciando…" }); + + const inicio = await sourceControlApi.post(rota, corpo, { + signal, + timeout: POLL_TIMEOUT, + }); + + const erroInicio = erroDoBackend(inicio.data); + const idGeracao = inicio.data?.idGeracao; + if (erroInicio || !idGeracao) { + vscode.window.showErrorMessage(erroInicio ?? `Não foi possível iniciar: ${titulo}.`); + return undefined; + } + + // Minutos de trabalho. Por isso o backend roda em JOB e aqui so se acompanha. + const estado = { falhas: 0 }; + + for (;;) { + if (token.isCancellationRequested) return undefined; + + await new Promise((resolve) => setTimeout(resolve, 5000)); + + const situacao = await consultarSituacao( + sourceControlApi, + ROUTES.situacaoGeracaoBase(namespaceReq, idGeracao), + signal, + estado + ); + + if (situacao?.situacao === "concluida") { + progress.report({ message: "concluída" }); + return situacao.namespace; + } + if (situacao?.situacao === "erro") { + vscode.window.showErrorMessage(`${titulo}: ${situacao.mensagem}`); + return undefined; + } + + const segundos = Math.round((situacao?.duracaoMs ?? 0) / 1000); + progress.report({ message: `${segundos}s decorridos…` }); + } + } + ); +} + +/** Marca todas as classes de um grupo como ignoradas, com o motivo */ +function marcarGrupoIgnorado( + testRun: vscode.TestRun, + classes: Map, + grupo: GrupoExecucao, + motivo: string +): void { + for (const classe of grupo.classes) { + const clsItem = classes.get(classe); + if (!clsItem) continue; + testRun.appendOutput(`${ANSI_YELLOW}${classe}: ${motivo}${ANSI_RESET}\r\n`); + testRun.skipped(clsItem); + clsItem.children.forEach((m) => testRun.skipped(m)); + } +} + +/** Reporta o resultado de uma classe inteira na Test Explorer */ +async function reportarClasse( + testRun: vscode.TestRun, + clsItem: vscode.TestItem, + resultado: ResultadoClasse, + classes: Map, + workspaceFolder: vscode.WorkspaceFolder | undefined, + documentSymbols: Map, + filesText: Map, + metodosParaDecorar: { item: vscode.TestItem; status: TestStatus; durationText?: string }[] +): Promise { + for (const diagnostico of resultado.diagnosticos ?? []) { + testRun.appendOutput(`${ANSI_RED}${diagnostico.codigo}: ${diagnostico.mensagem}${ANSI_RESET}\r\n`); + } + + const base = resultado.execucao.namespaceExec ? ` [${resultado.execucao.namespaceExec}]` : ""; + testRun.appendOutput(`${ANSI_BOLD}${resultado.execucao.classe}${ANSI_RESET}${base}\r\n`); + + for (const metodo of resultado.metodos) { + const methodItem = findMethodItemByLegacyName(clsItem, metodo.metodo); + if (!methodItem) continue; + + const cor = + metodo.status === TestStatus.Passed ? ANSI_GREEN : metodo.status === TestStatus.Failed ? ANSI_RED : ANSI_YELLOW; + const situacao = + metodo.status === TestStatus.Passed ? "passed" : metodo.status === TestStatus.Failed ? "failed" : "skipped"; + + testRun.appendOutput( + ` ${ANSI_BOLD}${metodo.metodo}${ANSI_RESET} | ${cor}${situacao}${ANSI_RESET} | ${metodo.duracaoMs}ms\r\n` + ); + for (const assert of metodo.asserts) { + testRun.appendOutput(formatarAssert(assert)); + } + if (metodo.assertsPassaram > 0 && !metodo.asserts.some((a) => a.status === TestStatus.Passed)) { + testRun.appendOutput(` ${ANSI_GREEN}${metodo.assertsPassaram} assert(s) passaram${ANSI_RESET}\r\n`); + } + + if (metodo.status === TestStatus.Failed) { + const mensagens: vscode.TestMessage[] = []; + + // O erro que abortou o metodo vem antes dos asserts -- o runner antigo o descartava + if (metodo.erro) { + const rotulo = metodo.erroAcao ? `${metodo.erroAcao}: ${metodo.erro}` : metodo.erro; + mensagens.push(new vscode.TestMessage(new vscode.MarkdownString(markdownifyLine(rotulo)))); + } + + for (const assert of metodo.asserts) { + if (assert.status !== TestStatus.Failed) continue; + + const texto = assert.tipo ? `${assert.tipo} - ${assert.mensagem}` : assert.mensagem; + const mensagem = new vscode.TestMessage(new vscode.MarkdownString(markdownifyLine(texto))); + + // `local` e null nos asserts customizados da Consistem: a mensagem entra do mesmo + // jeito, so sem posicao no fonte. O runner antigo descartava essas falhas. + if (assert.local) { + mensagem.location = await resolverLocalAssert( + assert.local, + classes, + workspaceFolder, + documentSymbols, + filesText + ); + } + + mensagens.push(mensagem); + } + + if (!mensagens.length) { + mensagens.push(new vscode.TestMessage("Teste falhou.")); + } + + testRun.failed(methodItem, mensagens, metodo.duracaoMs); + } else if (metodo.status === TestStatus.Passed) { + testRun.passed(methodItem, metodo.duracaoMs); + } else { + testRun.skipped(methodItem); + } + + metodosParaDecorar.push({ item: methodItem, status: metodo.status, durationText: `${metodo.duracaoMs}ms` }); + } + + const execucao = resultado.execucao; + + if (execucao.status === TestStatus.Failed) { + const falhos = resultado.metodos.filter((m) => m.status === TestStatus.Failed).map((m) => m.metodo); + const texto = falhos.length + ? `Existem métodos de teste com falha:\n${falhos.map((m) => `- ${m}`).join("\n")}` + : execucao.erro || "Existem métodos de teste com falha."; + testRun.failed(clsItem, new vscode.TestMessage(new vscode.MarkdownString(texto)), execucao.duracaoMs); + } else if (execucao.status === TestStatus.Passed) { + testRun.passed(clsItem, execucao.duracaoMs); + } else { + testRun.skipped(clsItem); + } +} + +/** + * Executor de testes unitarios da Consistem. + * + * Uma classe por requisicao, agrupadas por base de dados. O resultado de cada classe e + * reportado assim que chega, entao a Test Explorer vai preenchendo em vez de esperar o lote + * inteiro -- e o cancelamento interrompe entre classes. + */ +async function executeConsistemRunner( + api: AtelierAPI, + request: vscode.TestRunRequest, + testController: vscode.TestController, + root: vscode.TestItem, + clsItemsRun: vscode.TestItem[], + testesSolicitados: { class: string; methods?: string[] }[], + token: vscode.CancellationToken, + action: string +): Promise { + const unitTestConfiguration = vscode.workspace.getConfiguration("objectscript.unitTest", itemUri(root)); + const configuredTimeout = unitTestConfiguration.get("legacyRequestTimeout"); + const requestTimeout = Number.isFinite(configuredTimeout) + ? Math.max(0, Math.floor(configuredTimeout!)) + : DEFAULT_LEGACY_REQUEST_TIMEOUT; + const incluirAsserts = unitTestConfiguration.get("incluirAsserts") ?? "somenteFalhas"; + + let sourceControlApi: SourceControlApi; + try { + sourceControlApi = SourceControlApi.fromAtelierApi(api); + } catch (error) { + handleError(error, `Error preparing to ${action} tests.`); + return true; + } + + const testRun = testController.createTestRun(request, undefined, true); + for (const editor of vscode.window.visibleTextEditors) { + editor.setDecorations(testResultDecorationType, []); + } + + const { signal, dispose: descartarSignal } = createAbortSignal(token); + const classes = classesForRoot.get(root) ?? new Map(); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(itemUri(root)); + const documentSymbols = new Map(); + const filesText = new Map(); + const metodosParaDecorar: { item: vscode.TestItem; status: TestStatus; durationText?: string }[] = []; + + const metodosSolicitados = new Map(); + for (const teste of testesSolicitados) { + metodosSolicitados.set(teste.class, teste.methods); + } + + try { + for (const classItem of new Set(clsItemsRun)) { + testRun.started(classItem); + classItem.children.forEach((methodItem) => testRun.started(methodItem)); + } + + // 1. Plano: qual base cada classe exige e em qual namespace ela roda + const resolucao = await sourceControlApi.post( + ROUTES.resolveUnitTests(api.ns), + { classes: [...metodosSolicitados.keys()] }, + { signal, timeout: requestTimeout } + ); + + const plano = resolucao.data; + const erroPlano = erroDoBackend(plano); + if (erroPlano) { + handleError(new Error(erroPlano), `Error resolving tests to ${action}.`); + return true; + } + if (!plano || !Array.isArray(plano.grupos)) { + handleError(new Error("O executor de testes não devolveu um plano de execução válido.")); + return true; + } + + for (const naoExec of plano.naoExecutaveis ?? []) { + const clsItem = classes.get(naoExec.classe); + if (!clsItem) continue; + testRun.appendOutput(`${ANSI_YELLOW}${naoExec.classe}: ${naoExec.mensagem}${ANSI_RESET}\r\n`); + testRun.skipped(clsItem); + clsItem.children.forEach((m) => testRun.skipped(m)); + } + + // 2. Resolve o que esta ambiguo ou ausente, antes de executar qualquer coisa + for (const grupo of plano.grupos) { + if (token.isCancellationRequested) break; + + if (grupo.situacao === "baseAmbigua") { + const escolhido = await perguntarNamespace(grupo); + if (!escolhido) { + marcarGrupoIgnorado(testRun, classes, grupo, "Namespace não escolhido."); + continue; + } + grupo.namespace = escolhido; + grupo.situacao = "pronta"; + } else if (grupo.situacao === "baseAusente" && grupo.podeGerar) { + if (!(await perguntarGerarBase(grupo))) { + marcarGrupoIgnorado(testRun, classes, grupo, "Base não gerada."); + continue; + } + const idBase = grupo.base?.id ?? ""; + const namespaceGerado = await aguardarOperacaoServidor( + sourceControlApi, + api.ns, + `Gerando a base ${idBase}`, + ROUTES.gerarBaseTeste(api.ns), + { idBase }, + token, + signal + ); + if (!namespaceGerado) { + marcarGrupoIgnorado(testRun, classes, grupo, "A base não foi gerada."); + continue; + } + grupo.namespace = namespaceGerado; + grupo.situacao = "pronta"; + } else if (grupo.situacao === "ambienteAusente") { + // O TESTEUNITARIO e criado na primeira execucao sem base, dropando e recriando uma + // database e um namespace IRIS. Feito dentro da requisicao de execucao, isso devolve + // 504 no gateway -- por isso vira uma operacao acompanhada, igual a geração de base. + const namespacePreparado = await aguardarOperacaoServidor( + sourceControlApi, + api.ns, + "Montando o ambiente de testes sem base de dados", + ROUTES.prepararAmbienteTeste(api.ns), + {}, + token, + signal + ); + if (!namespacePreparado) { + marcarGrupoIgnorado(testRun, classes, grupo, "O ambiente de testes não foi montado."); + continue; + } + grupo.namespace = namespacePreparado; + grupo.situacao = "pronta"; + } else if (grupo.situacao !== "pronta") { + marcarGrupoIgnorado( + testRun, + classes, + grupo, + `A base ${grupo.base?.id ?? ""} não está montada e não pode ser gerada nesta instalação.` + ); + continue; + } + + // 3. Executa CLASSE A CLASSE, reportando conforme cada uma termina + for (const classe of grupo.classes) { + if (token.isCancellationRequested) break; + + const clsItem = classes.get(classe); + if (!clsItem) continue; + + let idExecucaoAtual: string | undefined; + + try { + // O POST apenas INICIA a execucao e volta na hora; o teste roda em JOB no servidor + const inicioExec = await sourceControlApi.post( + ROUTES.executarClasseTeste(api.ns), + { + namespace: grupo.namespace, + classe, + metodos: metodosSolicitados.get(classe) ?? [], + incluirAsserts, + }, + { signal, timeout: requestTimeout } + ); + + const erroInicio = erroDoBackend(inicioExec.data); + if (erroInicio) { + throw new Error(erroInicio); + } + + const inicioDados = inicioExec.data as Partial & Partial; + + // Impedimento detectado na validacao sincrona (base, catalogo, metodo): ja vem pronto + if (!inicioDados?.idExecucao) { + const recusa = inicioExec.data as ResultadoClasse; + if (!recusa || !Array.isArray(recusa.metodos)) { + throw new Error(`O executor não devolveu resultado para ${classe}.`); + } + await reportarClasse( + testRun, + clsItem, + recusa, + classes, + workspaceFolder, + documentSymbols, + filesText, + metodosParaDecorar + ); + continue; + } + + idExecucaoAtual = inicioDados.idExecucao; + + await aguardarExecucao(sourceControlApi, api.ns, inicioDados.idExecucao, token, signal, requestTimeout); + + if (token.isCancellationRequested) break; + + const primeira = await sourceControlApi.get( + ROUTES.paginaResultadoTeste(api.ns, inicioDados.idExecucao), + { params: { incluirAsserts }, signal, timeout: requestTimeout } + ); + + let resultado = primeira.data; + const erroClasse = erroDoBackend(resultado); + if (erroClasse) { + throw new Error(erroClasse); + } + if (!resultado || !Array.isArray(resultado.metodos)) { + throw new Error(`O executor não devolveu resultado para ${classe}.`); + } + + resultado = await coletarPaginas(sourceControlApi, api.ns, resultado, incluirAsserts, signal, requestTimeout); + + await reportarClasse( + testRun, + clsItem, + resultado, + classes, + workspaceFolder, + documentSymbols, + filesText, + metodosParaDecorar + ); + } catch (error) { + if (token.isCancellationRequested) break; + handleError(error, `Error running tests for ${classe}.`); + testRun.errored(clsItem, new vscode.TestMessage(String(error))); + + // Desistir de acompanhar nao para o JOB no servidor: sem descartar, o resultado + // fica orfao em ^mtempUTResultado ate o expurgo. + if (idExecucaoAtual) { + await sourceControlApi.delete(ROUTES.paginaResultadoTeste(api.ns, idExecucaoAtual)).then( + () => undefined, + () => undefined + ); + } + } + } + } + + applyTestResultDecorations(metodosParaDecorar); + } finally { + descartarSignal(); + testRun.end(); + } + + return true; +} + async function runHandler( request: vscode.TestRunRequest, token: vscode.CancellationToken, @@ -1341,7 +2198,7 @@ async function runHandler( // Determine the test root for this run let roots: (vscode.TestItem | undefined)[]; if (request.include?.length) { - roots = [...new Set(request.include.map((i) => rootItemForItem(testController, i.uri!)))]; + roots = [...new Set(request.include.map((i) => rootItemForItem(testController, itemUri(i))))]; } else { // Run was launched from controller's root level // Ignore any roots that have errors @@ -1354,7 +2211,7 @@ async function runHandler( roots.map((i) => { return { label: i!.label, - detail: displayableUri(i!.uri!), + detail: displayableUri(itemUri(i!)), item: i, }; }), @@ -1374,14 +2231,16 @@ async function runHandler( // Need a root to continue return; } - sendUnitTestTelemetryEvent(root.uri!, debug); + sendUnitTestTelemetryEvent(itemUri(root), debug); // Add the initial items to the queue to process const queue: vscode.TestItem[] = []; - const rootUriString = root.uri!.toString(); + const rootUri = itemUri(root); + const rootUriString = rootUri.toString(); if (request.include?.length) { request.include.forEach((i) => { - if (uriIsAncestorOf(root!.uri!, i.uri!) || i.uri!.toString() == rootUriString) { + const iUri = itemUri(i); + if (uriIsAncestorOf(rootUri, iUri) || iUri.toString() == rootUriString) { queue.push(i); } }); @@ -1390,11 +2249,11 @@ async function runHandler( } // Get the autoload configuration for the root - const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", root.uri); + const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", rootUri); const autoloadFolder: string = autoload.get("folder")!; const autoloadXml: boolean = autoload.get("xml")!; const autoloadUdl: boolean = autoload.get("udl")!; - const autoloadEnabled: boolean = autoloadFolder != "" && (autoloadXml || autoloadUdl) && notIsfs(root.uri!); + const autoloadEnabled: boolean = autoloadFolder != "" && (autoloadXml || autoloadUdl) && notIsfs(rootUri); const autoloadProcessed: string[] = []; // Process every test that was queued @@ -1409,8 +2268,13 @@ async function runHandler( if (autoloadEnabled) { // Process any autoload folders needed by this item - const basePath = root.uri!.path.endsWith("/") ? root.uri!.path.slice(0, -1) : root.uri!.path; - const directories = ["", ...test.uri!.path.slice(basePath.length + 1).split("/")]; + const basePath = rootUri.path.endsWith("/") ? rootUri.path.slice(0, -1) : rootUri.path; + const directories = [ + "", + ...itemUri(test) + .path.slice(basePath.length + 1) + .split("/"), + ]; if (directories[directories.length - 1].toLowerCase().endsWith(".cls")) { // Remove the class name directories.pop(); @@ -1423,7 +2287,7 @@ async function runHandler( // Look for XML or UDL files in the autoload folder const files = await vscode.workspace.findFiles( new vscode.RelativePattern( - test.uri!.with({ path: `${basePath}${testPath}/${autoloadFolder}` }), + itemUri(test).with({ path: `${basePath}${testPath}/${autoloadFolder}` }), `**/*.{${autoloadXml ? "xml,XML" : ""}${autoloadXml && autoloadUdl ? "," : ""}${ autoloadUdl ? "cls,CLS,mac,MAC,int,INT,inc,INC" : "" }}` @@ -1449,7 +2313,7 @@ async function runHandler( await testController.resolveHandler!(test); } - if (test.uri!.path.toLowerCase().endsWith(".cls")) { + if (itemUri(test).path.toLowerCase().endsWith(".cls")) { if (test.id.includes(methodIdSeparator)) { // This is a method item // Will only reach this code if this item is in request.include @@ -1540,25 +2404,32 @@ async function runHandler( return; } - const unitTestConfig = vscode.workspace.getConfiguration("objectscript.unitTest", root.uri); + const unitTestConfig = vscode.workspace.getConfiguration("objectscript.unitTest", itemUri(root)); const showOutputSetting = unitTestConfig.get("showOutput"); // Ignore console output at the user's request asyncRequest.console = showOutputSetting; - const api = new AtelierAPI(root.uri); + const api = new AtelierAPI(itemUri(root)); if (!debug) { - await executeLegacyRunner( - api, - request, - testController, - root, - clsItemsRun, - asyncRequest, - token, - action, - showOutputSetting !== false - ); + // Escape hatch enquanto o executor novo esta em validacao. Sera removido junto com + // executeLegacyRunner assim que o fluxo v2 estiver homologado. + if (unitTestConfig.get("usarExecutorAntigo") === true) { + await executeLegacyRunner( + api, + request, + testController, + root, + clsItemsRun, + asyncRequest, + token, + action, + showOutputSetting !== false + ); + return; + } + + await executeConsistemRunner(api, request, testController, root, clsItemsRun, asyncRequest.tests, token, action); return; } @@ -1604,7 +2475,7 @@ async function runHandler( let currentOutputItem: vscode.TestItem | undefined; // The workspace folder that we're running tests in - const workspaceFolder = vscode.workspace.getWorkspaceFolder(root.uri!); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(itemUri(root)); // A map of all documents that we've computed symbols for const documentSymbols: Map = new Map(); @@ -1839,7 +2710,7 @@ async function runHandler( } else if (debug && queueResp.result.content?.debugId && pollResp.result?.content?.debugReady) { // Make sure the activeTextEditor's document is in the same workspace folder as the test // root so the debugger connects to the correct server and runs in the correct namespace - const rootWsFolderIdx = vscode.workspace.getWorkspaceFolder(root.uri!)?.index; + const rootWsFolderIdx = vscode.workspace.getWorkspaceFolder(itemUri(root))?.index; if ( !vscode.window.activeTextEditor?.document.uri || vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)?.index != rootWsFolderIdx @@ -1931,32 +2802,33 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di const testController = vscode.tests.createTestController(extensionId, "ObjectScript"); testController.resolveHandler = async (item?: vscode.TestItem) => { if (!item) return; // Can't resolve "undefined" + const uri = itemUri(item); item.busy = true; try { - if (item.uri!.path.toLowerCase().endsWith(".cls")) { + if (uri.path.toLowerCase().endsWith(".cls")) { // Compute items for the Test* methods in this class await addTestItemsForClass(testController, item); } else { - if (notIsfs(item.uri!)) { + if (notIsfs(uri)) { // Read the local directory for non-autoload subdirectories and classes - const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", item.uri); + const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", uri); const autoloadFolder: string = autoload.get("folder")!; const autoloadEnabled: boolean = autoloadFolder != "" && (autoload.get("xml") || autoload.get("udl"))!; - const workspaceFolder = vscode.workspace.getWorkspaceFolder(item.uri!); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); let workspaceRootItem: vscode.TestItem | undefined; if (workspaceFolder) { for (const [, root] of testController.items) { - if (root.uri!.toString() === workspaceFolder.uri.toString()) { + if (itemUri(root).toString() === workspaceFolder.uri.toString()) { workspaceRootItem = root; break; } } } - const entries = await vscode.workspace.fs.readDirectory(item.uri!); + const entries = await vscode.workspace.fs.readDirectory(uri); for (const element of entries) { - const childUri = item.uri!.with({ - path: `${item.uri!.path}${!item.uri!.path.endsWith("/") ? "/" : ""}${element[0]}`, + const childUri = uri.with({ + path: `${uri.path}${!uri.path.endsWith("/") ? "/" : ""}${element[0]}`, }); // Aplica o filtro dos roots configurados @@ -2083,7 +2955,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di // Update root items if needed e.removed.forEach((wf) => { testController.items.forEach((i) => { - if (uriIsAncestorOf(wf.uri, i.uri!)) { + if (uriIsAncestorOf(wf.uri, itemUri(i))) { // Remove this TestItem classesForRoot.delete(i); testController.items.delete(i.id); @@ -2102,10 +2974,11 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di // Determine the root items that need to be replaced, if any const replace: vscode.TestItem[] = []; testController.items.forEach((item) => { + const uri = itemUri(item); if ( - (notIsfs(item.uri!) && e.affectsConfiguration("objectscript.unitTest", item.uri)) || - e.affectsConfiguration("objectscript.conn", item.uri) || - e.affectsConfiguration("intersystems.servers", item.uri) + (notIsfs(uri) && e.affectsConfiguration("objectscript.unitTest", uri)) || + e.affectsConfiguration("objectscript.conn", uri) || + e.affectsConfiguration("intersystems.servers", uri) ) { replace.push(item); } @@ -2114,7 +2987,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di replace.forEach((item) => { classesForRoot.delete(item); testController.items.delete(item.id); - const folder = vscode.workspace.getWorkspaceFolder(item.uri!); + const folder = vscode.workspace.getWorkspaceFolder(itemUri(item)); if (folder) { const newItems = createRootItemsForWorkspaceFolder(testController, folder); newItems.forEach((i) => { @@ -2175,3 +3048,131 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di }), ]; } + +/** Uma base montada que atende alguma classe de teste unitário */ +interface BaseMontada { + namespace: string; + idBase: string; + tipo: number; + tipoDescricao: string; + revisao: string; + qtdClassesUT: number; + usuarioGerou: string; + /** `null` quando a base não registrou data de geração */ + geradaEm: string | null; +} + +interface ListaBasesResponse { + versao: number; + namespaceSolicitado: string; + bases: BaseMontada[]; +} + +/** Monta o rótulo de uma base no QuickPick */ +function descreverBase(base: BaseMontada): vscode.QuickPickItem & { base: BaseMontada } { + const quando = base.geradaEm ? `gerada em ${base.geradaEm}` : "data de geração desconhecida"; + const revisao = base.revisao ? ` · rev. ${base.revisao}` : ""; + const quem = base.usuarioGerou ? ` · por ${base.usuarioGerou}` : ""; + + return { + label: `${base.namespace} — ${base.idBase}`, + description: base.tipoDescricao, + detail: `${base.qtdClassesUT} classe(s) de teste · ${quando}${revisao}${quem}`, + base, + }; +} + +/** + * Lista as bases de teste montadas e permite regerar a escolhida. + * + * Regerar EXCLUI e recria o namespace a partir do checkout, exatamente como a opção + * "Regerar (B)ase" do Gerenciador de Bases. Por isso a confirmação é modal e o default + * é cancelar, espelhando o "(S/N): N" do terminal. + */ +export async function gerenciarBasesTeste(): Promise { + const folder = currentWorkspaceFolder(); + const uri = folder ? vscode.workspace.workspaceFolders?.find((f) => f.name === folder)?.uri : undefined; + + const api = new AtelierAPI(uri); + if (!api.active || !api.ns) { + vscode.window.showErrorMessage("Nenhuma conexão ativa com o servidor.", "Dismiss"); + return; + } + + let sourceControlApi: SourceControlApi; + try { + sourceControlApi = SourceControlApi.fromAtelierApi(api); + } catch (error) { + handleError(error, "Erro ao preparar a conexão com o servidor."); + return; + } + + let bases: BaseMontada[]; + try { + const resposta = await vscode.window.withProgress( + { location: vscode.ProgressLocation.Window, title: "Consultando bases de teste…" }, + () => sourceControlApi.get(ROUTES.listarBasesTeste(api.ns), { timeout: POLL_TIMEOUT }) + ); + + const erro = erroDoBackend(resposta.data); + if (erro) { + handleError(new Error(erro), "Erro ao listar as bases de teste."); + return; + } + + bases = resposta.data?.bases ?? []; + } catch (error) { + handleError(error, "Erro ao listar as bases de teste."); + return; + } + + if (!bases.length) { + vscode.window.showInformationMessage( + "Nenhuma base de teste montada nesta instalação.", + { modal: false }, + "Dismiss" + ); + return; + } + + const escolha = await vscode.window.showQuickPick(bases.map(descreverBase), { + title: "Bases de teste montadas", + placeHolder: "Escolha a base para regerar", + matchOnDescription: true, + matchOnDetail: true, + ignoreFocusOut: true, + }); + if (!escolha) return; + + const alvo = escolha.base; + + // Modal com o namespace na frente: um clique errado no QuickPick não pode derrubar uma base + const confirmacao = await vscode.window.showWarningMessage( + `Regerar a base ${alvo.idBase}?`, + { + modal: true, + detail: + `O namespace ${alvo.namespace} será EXCLUÍDO e recriado a partir do checkout.\n\n` + + `Todos os dados atuais dessa base serão perdidos e a operação leva alguns minutos. ` + + `Não há como desfazer.`, + }, + "Regerar base" + ); + if (confirmacao !== "Regerar base") return; + + const namespaceRegerado = await aguardarOperacaoServidor( + sourceControlApi, + api.ns, + `Regerando a base ${alvo.idBase}`, + ROUTES.regerarBaseTeste(api.ns), + { namespace: alvo.namespace }, + new vscode.CancellationTokenSource().token + ); + + if (namespaceRegerado) { + vscode.window.showInformationMessage( + `Base ${alvo.idBase} regerada com sucesso no namespace ${namespaceRegerado}.`, + "Dismiss" + ); + } +} diff --git a/src/extension.ts b/src/extension.ts index 3ac8286a..7402b7ca 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -135,7 +135,7 @@ import { FileDecorationProvider } from "./providers/FileDecorationProvider"; import { RESTDebugPanel } from "./commands/restDebugPanel"; import { modifyWsFolder } from "./commands/addServerNamespaceToWorkspace"; import { WebSocketTerminalProfileProvider, launchWebSocketTerminal } from "./commands/webSocketTerminal"; -import { setUpTestController } from "./commands/unitTest"; +import { setUpTestController, gerenciarBasesTeste } from "./commands/unitTest"; import { pickDocument } from "./utils/documentPicker"; import { disposeDocumentIndex, @@ -1526,6 +1526,10 @@ export async function activate(context: vscode.ExtensionContext): Promise { + sendCommandTelemetryEvent("ccs.gerenciarBasesTeste"); + await gerenciarBasesTeste(); + }), vscode.commands.registerCommand("vscode-objectscript.ccs.activateNamespaceConnections", async () => { sendCommandTelemetryEvent("ccs.activateNamespaceConnections"); await vscode.window.withProgress( From c498f91addb5777710ed63b5f78ed1dd391e2787 Mon Sep 17 00:00:00 2001 From: Ricardo Junior Date: Tue, 15 Sep 2026 14:37:36 -0300 Subject: [PATCH 2/4] feat: nova logo --- images/logo.png | Bin 21845 -> 11652 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/logo.png b/images/logo.png index 82d5be74b96eaa3cae676567e337bf37c19efeb3..146e8c9d4309e211bbf03b1befb1ebb1af23afa1 100644 GIT binary patch literal 11652 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 pjO~c_7EvPp|Esvve`H;_-2E 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 bvleGh8@L+IZ Date: Tue, 15 Sep 2026 14:39:04 -0300 Subject: [PATCH 3/4] feat: novo banner logo --- images/logo-consistem-horizontal.png | Bin 8978 -> 58174 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/logo-consistem-horizontal.png b/images/logo-consistem-horizontal.png index 29b42c8bd3d805e51d83501511db37be44a93422..1e98201e3455c00e168757403d1bc5374381fcee 100644 GIT binary patch literal 58174 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 z zoNG_yz)bx-qMHI8GpUjMy)k{)3N?=9K1bfX=SDHq9ne0X#DS??Lh#4@%6J%H#W8d+ z)(l@zfc$5@KYOII_GW=uosAqzk|*ZzdK>{HoP#Oe0e+x$h(dn_% zPLFL20FnM4T)dxm$mMZ>O>h_MB;OUlb(KoeRW3Q21 zrc}g{Mf;a{vUD$#Eh|DMDJ zE3q$cSb})j9wZ*W#Ftn$kn->qP|e?gj^i{+Pngo?JF8r+33N#rU^MaNV2`HE-Ukp# zvu5SgR_mi+PawYBEcjkK{N`>wL84wY^AZKcL#}J6G?9*oL4R3P_M#|j_t=%`O zAom{9lpc>kg&dET*;jJd*=XhB0x#H>G+DZpBV+=~A}R%5PxuDh!a3k-Zgc2AWqd(% z5OMrh+t`pm};(TwI(do!`pV zA&zTbq3RzRZYpy*ueF4+y<_Mjr~6s7!S>sZjI6Bp-72FU$j~06AlMTz$~v%Sje0jJ zBt3YeY+`hDG>V$Ienf?89|yr7loC8#)Nu^d2EM7boT8{65oo4FB9YXwi{%D+DdJS4gabjSy587Cgii-&CIZRYF`(^*o#!)Nx05yi`PUAu3O5)1{bmwN5qU6MQ=oHD zRD&)?UC1K7pCB*bJf)=eIxf&A;XuG#H&HKdS(ZEkM+I;O6Sa~w((&PiK@H706o!As zWOcJw9F}F3=nt$=>#yL0D|Pmn6G`bN0*tmXMV(DZlWX_wkYB|I`86V>o{9#3na^Q@ zvhUBqdOJp0t$NEsuR<>=IgO|q9}8!^t{Hzs_jg#KRlJvX)8kA{s$1;6{^z- z3)749pxRTjrU9J}oq2{NMQ~(@QcK{i0u7Ti5|zX>LWF?xq=!=B&S$#MJxIP6>SYc4 zFlps1S<+BQzo_d{qm_vMM(#60E;Q{vi;kNQoww8GC)IdoT>?QqXQp&&wYx%D>p-fy cS9WMmLIU-opN&n-Z|%~guBwfEr(*r!e}=6|jQ{`u literal 8978 zcmb7q=U-FJ^ERDOjocJz(t8jEsZtERNhchN5F-I0^xh!|A|fCNp(s^BN`%lmgeJX1 zP!Lh1H|aQFri5WXMhe6VCn4=5d6pwNd|THMj-hOJRi9tEs&3#Lw$#ksw5^kGh-_qQu*)y z^J&Uli6z0n0Tw2fWMKZ=_s!rwF{uUBT@%Z{gt%f~kdX=68fZhULKgP&zQn#42kSgE zyY74OuR1SbYrCq-B{AlHcl&vZiAdLO#W;R_i(v=qz)kY%8?pSHKi!q9 zrWfEYP;f?d_uF}16&LOA0-)hz>ZToqJg&i5esNx#vsUX5(Ax~Lg`z%z59b5PZNXIX zv6VZmRct8&R8R<$=@0rE2q-pWd+fA~dA3UntUc@JXgX4Siy@Xjb~b8`KdzS4y(R8H z?8C86AgGzyOz50Y5(_OT_RsoUkadZFclLeoY)t6s$e^Y$36m9ytfCJzxxopFjdEA5 zQbEF^SG)c;)ZQ{G1y4 z-MXZvQs5x+f9*=vJY5Uc**Jc7DzwcURdum~!2jbof_8_1!rpCrOugb!{P$wO^M)-btZ3JWV&BQmY>^CFZJnp*=ePULiAwzktLEh5 z?EDt^j6#EM=@0*=XN8}}2lg0xehA8u0p+brxhK^4WpzX2-dlO7%<`{=Gd<`8t{lA)aS-Kz_{9p zH)7x~h_QhBKpOIL$l5v1#9*9VYHR{lT1>2giD%mO{- zF>wm)E~_2|j%LALJwS#5o6{JAb>{=p57)Yuxq`*2UfUoNI}V40;{AzU@R^5FnPFbt zCM@{jC7#dt?JcD82R_>fcyjo{lSUAhZ&?|okyncVWpV+2>}jDDVfFK&Yl4Y`7Roc{ z+0-$aw&$OyF)G?o0^C6RcE9HnF2L?~!%;8K`o~d2CalpoKUe;p8b9C6=U4&7c)v0a zI8qdugGbn?EkY>(%5&Xze>YB`&A|{mkt>0Aaosa z4WPTz<3COd8&2+Zl4*p(ui}hN-n1wWD3#yU5ziF*OX?tW-nqDYv+Ymb8yKegDwsG@U9 zqJ2zegyqFIS8}O~zfW7t;UE3(yor|i&tXv!=lrn(tN;9^Fbtyw+%K`w3RLIMPiq*C6*8y0lh zvcq)iVsxdk(AzbBF260cO4`$_pf_H?arI7T4-NTf#wa)Mo*X9ZA4H&Ua zA^8)|rRDb8YVYs88g>JNMNND3;AiQ5{_DWwG#aQsGj6)Fs2p#o7%+ZE%qY9ly%AOy zCpxt&7IrAwt5T^oEpI8g!z&kJy_x(;0k4@VuZ%ZL@iRjskp{-pgzUY9Mp}qX^0+*%Uvx}<_%yXjCN<~APa$t(4wL1Lx-1$aN9oJs%ddYE zSW>CfY-BZDR|yFOPz{6yJJf^+ye32^a%H~YoD;urnT}V9lQ5=&9qk_sT=QfG>Kt4= zCg4sD0_PMqbbnh@5#hNuZ5K_{F4Zocsm|~;*reBq)5EmP>1TJxgeHe3yhk)NoIIGX0679MYHE;W_M?a9h^<6teyQ@n_jmsP5ANYOzQ_S zsqxCCMa#S2Qtv7h7zvv`!K*y+ww2~i!>gpezI58tNpMqZkJ>Ek@Ozo?FO^mU$NYf3 zyH&YAHUnkCxcOTco*at)?%}9xZ~B)~+9`E1z_Fj}xPOr$mk};9r|>FfEfLf3z?h;-L^8cqanEt|aS6~6{@e5BNmRZ` zjPTL+ENhY*W3p$^nTTnNm^uF`Vx=45>l!(VE@9>$uhE#11_}NE zLTF#}@EC9-=_iJe@Fdl3CK*x5JX_Eru0 z@vCqZoOYG31=Tr!=ThChn^K01qF!}!XUkmZl74#8^S8DZHF_bL-{m|#s33F;AtuxK>6Qt7DvbW2^JBbS$uorrJ2jWexzS3CkI-O) zSA7lcBnB@98vHnYRiP3JN|0Bo1 zhh0w2T7wyvItq`M>w56D)IA_K{IoYMOeS+`;*$R=5l?Jj(N0=Y!=!9}TCARZnDkUC` zB*)6)Upzd4=5N=m|0rDo!xFuimfN2Ul>++ZHvUY=SVw;uf? z$tIv!nu<4va^H~vci_@1P{;>#U`l-G8qKgomP23`hF2IKy4?#ifaka)fSnOETlKtf zC;InLZqXMotY$6E8Da zz`Z9&h0*}?BWRuGN{rQK{bt#| zRi)C2ZSjWbkGO%Btkcm5=0#p#Iiulc%}W<*C~iSA)U8(=`l`8d9oztw#VdtPY1G`g z&6;Gxtj(7xt5l^puN^&=I+a%nP(|D)EMv%mZO<;peR2Y9fxCnS6yRwoJTm|8;?uu&r=-(-m7E3?F z8khIqqR+e-kAQ}p7`EebVtDv)e(u~B_flM7uZ4qo8f0kL_okON!-^Xe)kDdxwUCCwzyrp{UK$04I%>58*d!(!*Pg_SrMZN62%9)~K7$vYuX-ZLY8>pEeRw z?#;i0oAqShVmYU~(&-XyX%x63y!Dr4f&@c!_n$+Nc)tkV$1J+1?;}&ZIDnE^z|KcW z>_UlNXXhaeW~RE%3La__?=6czqP8J)LU5{Is=f)9xI zcvfv~^@0Z#>d2(H5O{0dn54~b>af+01@|ZPgQqwh#Jm5jrTrQ(%=wVyCbx5I7nEZw z9>O#2h<-(fe?Lvye{R3!Iu{ztgyFq&8FI`7 zl^-%y5w^YlPX$E$wn%}a&xE_*5!-IgOtZSXl9iW| zD3knZ$0|6;Q0E=j`^(?H?f3*zdp<{n8@5s~w)k1T&lpAP8epIo@bdTe)GOj}wI@Gi z(j@ciNBfg#9hC(PMlOSbP&9NDHF3X)^#d#F_}OOoQL#5ApraBjSsw7M@3V8?W1rv* zY})Q%2=lAu(Nty#OHCP)nGt?^X$`Wqz^8-QX$`03JKnp3RuiS==Ls~p7uq!$R%>OQ z8<#BW>#rx0C`W!aNekiyS(2<0#yKbR36K9|+tI)ZP239;d@wPbCFKRvAe_fzJeL<4 z!PiD*P6HK+Ur{{%NtVPi&|0;l(s016y*ueV5QJtM2H(pMN!RG7(Ir0N1ayk!Ebt3n z3W4ofHtay!rJFdiDBmTj1B7dR^kJ-IQ$VY9$hdwlJAqM(f&fNL8-IMu^E6tY3DH1- zW#LFStm(IC$|eN|or0#gtUGHvK5s4#V{}>$_p_R&l8C>B(lW65ifrU4W}VET65xl+ z-C2H^6kf9CMoGJ3cwPC=2iOd1%rGtH7ydw)3Twj4qRW0y{EMYQ%c(R4ih<@P)0C3R z(?^mI`W)r(l3@1q7pWSQkU2qIHV0cRdhT@Ah#vL@GORD$e@6j?mAk=ccJ~;;yfLkd z5XbVuZONn>vp*2g{iyW;TG%{OAAd-p;uDL7U{S7MBY*shnpJ-4(WXJCym}Agd-(Kf z&oExx?9#Qj7XtDGQuOLFpMrht`W24I8#Xqw%q7G$Xh-#GmTPe#o_0=&>S*Cvy7V~l z=_k$AJ_?}xz5NbV&^<1R+{CSSSCWcMtnlEu8o@lqeMf7rG-F9 z(zm*Ng_WPxL2zX$0*SyWnG~1VH<0;|(5H7#{uQ6ai0rX;G6To2H zO4ih-+!}>F1Lnx29A^x~`z;PNlnuN)B;a)Cyt+n0bBuDE6FW2;iVC%M0v4ML0 zFnPS-+g#;$>~)D^$ zzV#A=ICXs+RzK3YK!NdCWIuv@H%(WhFiIj%a#-XCf8ahDGFj7q>yp}k^*|2q_vewR z{#DhT55B^y*Wb`$o@DF3kS=A1TSsGVvrhRiRp}#&uF>|Klfp22+*mSM(yYA-sCKr;13+{M*mZuFGV& z=kr>l0{s8IzzvezD_Vpt#HnwRrTUIUQR2$ch{QR8XE^^)T_dzHcNyh;C7j@6=BH{Q z8=py*6qXcG#ySG#0If+Lv!g{=L$cb|QC(k@k8@SomW_VPsE_{^#HdHD-pj|1xR|#` zE+-Jtf#Bu%!#5)tf{Pl;-0K+5(-3Uq{8BKm=F&Oj3C`Cra|MaRT{eD z*;Tv{$tCEuGy0|lG#dj|+*Qliu~x)P2Ds0i4YAx26AvI-aH2#jWK{{WOZn?6}*%t$U9Uxs<-}ONvZxsvl!@ zo5OA0rqrM)@gfcxv$jjAFvo$*$ug4O8X+iU2CZ=Vco zTo!^Z@GtHjOHo2xp*o>(<#(#6B|KuS%k+bm>kVD5AGOub(3!R=B_h)4KhtoRk#A>h zdI)+3_-Ah}*xV^URv&C&RK}uw1Zl0wc2;+vS6x}Q1SMp??o^RbxDW#yRaJD$!4ygV zaeSxUUNf@bkF3P6hR-=DFgqKY+%fV_jG#F(Y?|2+=NFA39l(*&U3fH0HzP3mN`Uiy z(0beO8xqpO$#;?TG2QJFRdQ?L`TGeY^2!a!H?Ek5gVHRa(lFU$a|MOufD!z#1pgkj zSn&Ws!W)rHjR?1*L}AD=)j7)+EPy0WniS!?$KwbeCOiToDpPOWcA(y4Q95#&D3T+5 zl5BmU5Oo=-SL{aFm8?5xbYcV;9-k7(Af=0wGwhxdUt=xv57iOUwR09% zItRI{YpRC=OU^FB*88HABQvAkGIUrUxgG?5CBUST3^-)yTwxck1R8}L&I0e6V6D1P zLhY(4Z@cj;k$>KfFGx7-cfQ6BoH%KOkVcadLrR1n+T*9ct|^$qcKeX|>sp8LeJaKK z^)Z|lonZlMzaFt%4<3*m#GkHIdWzFPnBXOQR@Zm(e1i9e@)^P-R5}ZQry&_b^sdxE z*SkW?P0>RLDXWsxAlos3UvCrAJ2!Q|jtOq<&sB+{s1f~=-FB|SKEawxA>R-^X6HKk zeN!8~=CrOkp7B_T!kcTBAtlD`v@ixdNSi&rcs{>g@N}aC%Rco&23rUH*rL@Ye0L}S0lER zv-TNAlu;mXm3hEajI>bmbt5Kq&7PwBaudB%^`NG`J`#xJ1pnd9${RJRmnD0BXr06; zs=+(QXZf}u;A;H1}>h&ZL z7cYMmk>ScVR5CWAO;bKT?)6Th$3|^*Ap2{&_-HW(TvHMsKqKd0yPS^Bm=QGy9oL-J z@rNsIp&p()dG+etp(A(snNZ$?)a-Y%DXM~ zZXeF!Jw5B?s(LR2HYva+Ub~5iKJY_a{j*?jnAjbIe}CoX_96*|ua%8K{4_y7k`iA!!rhC)@YRU; z-MObZw{|z4D!FOJXBnG~7j1oCzOMb%e2fzF(`HAQ;rA|pCuXT980NB}8t^L)2Rf&_ z<#)2x){{F2#DtkrSt{kfbKO&QPF{a+x^O4s@jZ+ql}%Bw=zkiE%6Kk^XkP6BO*_Lo zaOICBuhP~6YZBbinLIu8!ltDsYSH24ruudwrGKN=aDDLcIKhTP^E9FcXcV zT4eEDCMvjymyPPq5=L@7@@?BcqO}^&TRyw?oDpy1#`{>E@Q;O-$yIbVt_}qe{qJtL z*6@&0tN8|*v>KbrbD{KN*Y_XJ{ILOYzlY;wGvq=;C>7Rz5X=8lehsF9@n^Y-GFSD8 z4%Fc-INJ>lqTl&%4b(b1QemImK;6m`dY?--ADI1xmzgW0u_$^1*qLuYRUa1p?3XTW zBL`}pa=f@P`1$n2Q}ARaY*Mj>AJ_9V+-v0g8u!NJgmNo)OyHB+_3txV_x|9|qTh9` z3^;RC&!n*-kvos}DjFJVrmTX=q@>A4+rK9rN#7{VPx*UJ+A$LJf9FXd zGU2JtEb_md2_7ZoXx>G5Ner7CU-9QfO$Q+c0HG|oILCm6P!6eG+h~`#&$z3&#cH9s zA772!KNpLc8*?qs;tB-y#jw%D8m`MN38y0IkYSFxL?cytY~23f9jTnaEKAjI=cTyt z+y96=4;QL%#63FrkS*^^8%ZWC-1F)7`@~c4&|OQ`Fbln&$a6NS-UwDeVku2Xop*;P>ZG%E&HYI?&#=E?SDd}M0|C;2+)wQ5hZJn%|qH=|0BWMj=ezwb#t zuN{mc9#krlk8oHmzxF8*sR?+to*l2LOE54s8Kf9Vq8#WkPz)m6q69R;5Ol}e2Jn&~ zJ?0yoUxl+qesCjvYcUOTkKm-EOeR5dpON@oLGaN={WJ>=DXjsG}SUL(zDTG!Ve{K3R;=+ zK)CGisg~6PqA0v1SnuqM1|2fBN@-z}&Y(-H!<#;PpOZ( z9QwA>pWYuS2n~%2*H@L z(;fCCS4Zp`JfF!#3&b>EIWdnQ#HA?I~eIGzS3)??GHu?x$OuyU+fy>G+* zL;kgXbs9(!n4q{S9LV+D>1?g0_Zv%Jtwp8&;CfDdjYbRd<5ZnwuveMQg3`iS9^uJc z@UL_WJMK#(T~y$NiF}#u&fZQ7YU)Rv_|o8;v}4JZcdc7;f45&n{N&)y$c<_^%+KJu zvsdJDTHFaFNv)_qdM>c36J~l^7DPoRO>-mGagEqT11$fzi(Iu409Dv5GN4b_!0wDP zR2@cF_AC1v8esKTICNptmb^{W)o~Il4M*&ERv0t3+Af(*ivj9GkD?_oCxA(f3KS3~ z65%L)EC_#q5L+Z2n$Jk6I#Ob*$p|Ut@r6z95e&G_TkWdmvh>hjbJOb8DZj?|UJAq) za#UU|&|D_Bl|6|m3{I#y;~?9qjK4uaI%Gb(4cnOmi07tC=4fiDw+*Rs?7dp7!%NlS zluY~W121_nh{)gYP=nYn^%Nafm^#Vr1zWoP5`4nxu3;Ur2Wr-u902c7Rr7<~b&@$0 zziTs7p@FdjdTJ&x6#{JoCe^;PQ9=0(AP4lZ_y70e2Q#$C@^8u|QaeJy&*zLg#6zf| z1=v!Pt-?;;SoJ)hG4FVsgo$jo+fsNMB@3o%n4Y?JMdSxL;;x55|U4LtnM0S(^%@;z^{Lw!5AmIJVHGUiBA8@fm$=A?%N42njJI zubLxsS4kNJ1siRbk4Le$KtZt!_t|~^r)iL)iXSw5pO6>n$|%W6BBycRELVN1Kn+^p zjE_<3!&!;r;?6Pr`onS%H+`GM5l{`;Xfx6+pW75ckQzdse@F2`>qX d{W}*u_UzpVUk?L6=>iIwfsToGl_os){{T Date: Tue, 15 Sep 2026 14:45:55 -0300 Subject: [PATCH 4/4] fix: logo no readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f869068d..0f450fad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Consistem + Consistem

# Consistem ObjectScript extension for VS Code