mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-14 06:57:51 +00:00
* bots, nodejs: document chat, connection, member contact and network commands, typed commands in library * bots, nodejs: restore apiConnect, wrap domain once, test all connect responses * nodejs: JSDoc for new methods * docs: plan for bot API commands and typed Node library methods * tests: channel prepared with a domain has a verified SimpleX name * docs: apply review fixes to the plan for bot API commands * docs: apply second review pass to the plan for bot API commands * docs: refer the bot API plan to the rewritten test commit * docs: record decisions in the plan for bot API commands * docs: plan tests of the generated Python command strings * python: test the generated strings of the documented commands * docs: plan for files, /leave and library methods in the MCP server * docs: wait at stop in the MCP plan * docs: library methods and /leave mechanics in the MCP plan * mcp: typed library methods for chat commands * docs: plan the message information command for bots * mcp: /leave CHAT owner command to leave a chat at once * docs: file servers, GIF and video choices decided in the MCP plan * docs: file mechanics in the MCP plan * mcp: files of received messages * bots, nodejs: message information with the file servers for bots * docs: plan the relay approval option of receiving files in Node * mcp: download of received files with owner approval * docs: name the xftp-server test helper in the files plan * nodejs: receive files without approving unknown relays * docs: plan the user servers command for bots * bots, nodejs: user servers and relays by operator for bots * nodejs: document the message information method * docs: plan the file upload progress event for bots * mcp: send files with owner approval, refusing private keys * bots: file upload progress event for bots * mcp: /leave template in the agent control chat menu * docs: sent copies are deleted for contacts too, at sndFileCompleteXFTP * bots: drop the file upload progress event * docs: drop the upload progress event plan * nodejs: document user servers and relay approval of received files * mcp: download cards name unknown file servers after the upload * docs: record the wait for the file description in the files plan * mcp: fixed wait at stop for cancelled cards, without /sql * docs: record decision C4, a fixed wait at stop, in the files plan * ci: test the MCP server package on pull requests * ci: state when the MCP workflow can be dispatched * ci: run the MCP server tests when its libraries change * mcp: log the errors of image previews and resizing * mcp: keep files in transfer in the data folder * mcp: restrict sent files by the real paths of refused folders * mcp: validate the file at the send by its inode and change time * mcp: limit ffmpeg and ffprobe to local files and no input commands * mcp: ask the owner for unknown file servers; read approvals at creation * mcp: name downloaded files by their ID; return paths of downloads only * mcp: end each download with a result for the agent and the card * mcp: show files above their sender's size limit as failed * mcp: refuse empty files, as without file and captions too long to send * mcp: tell the owner about failed uploads and unposted received files * mcp: delete resized copies left by the last run at start * mcp: name downloads and leaving in the help and greeting, align docs * mcp: compute the download path for received files only * mcp: send a /receive file to the owner under its name * mcp: keep files received without a download in the chat's folder * mcp: keep a left chat's files and delete them with /delete CHAT files * mcp: test that sent images are resized by the core * mcp: wait 30 minutes for a download's progress before cancelling it * mcp: end a download as deleted only when its message is not found * mcp: delete the copy of a /receive file whose post fails * mcp: count the quoted content in a message's length, as the core sends it * mcp: name received bytes, unknown-server downloads and left chat files plainly * mcp: test refused folders, file changes and download approvals * docs: align MCP server docs with refused folders, leaves and download waits * mcp: move received files only in full, delete partials only of downloads * mcp: report a received file missing from its folder as failed * mcp: move received files of a group's member chats into its folder * mcp: read a video's preview and duration as an MP4 or QuickTime file * mcp: 0.2.0 on simplex-chat 7.1.0-beta.10.1 * mcp: file folders by chat type, /delete cancels downloads, files reply * docs: MCP server specs and plan for 0.2.0 * mcp: /receive gives the agent a file, /forward sends it to the owner * docs: MCP server specs for /receive and /forward of download cards * docs: MCP plan records the temp folder command as a library exception * mcp: /delete cancels resumed downloads; /forward quote fits the limit * docs: MCP specs and plans for /forward quotes and resumed downloads * docs: MCP specs name the source commit they are checked against * docs: MCP PLAN states the /forward quote and move rules * mcp: a download moves its completed file whatever its size * mcp: /receive N all covers its card and later files, not pending ones * docs: MCP files approval scope, reply text and README server wording * mcp: a download approves relays only for a card naming unknown servers * mcp: files in member chats of a group are tracked as in the main chat * docs: MCP relay approval of downloads and files in member chats * mcp: a rejected file gets a card again under the files approval * docs: MCP rejected files under the files approval, README scope * mcp: an offered file without a description gets no card or action * docs: MCP a file without a description is not downloaded * mcp: 0.2.0 on simplex-chat 7.1.0-beta.10.2 * ci: test the MCP server as a scope of build-linux
118 lines
9.3 KiB
TypeScript
118 lines
9.3 KiB
TypeScript
import {T} from "@simplex-chat/types"
|
|
import {chatErrorText} from "../../src/log"
|
|
|
|
const server = "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion"
|
|
const relay = "smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im"
|
|
const agentError = (error: object): T.ChatError => ({type: "errorAgent", agentError: error, agentConnId: ""}) as unknown as T.ChatError
|
|
const broker = (brokerErr: object): object => ({type: "BROKER", brokerAddress: server, brokerErr})
|
|
const forwarding = (brokerErr: object): object => ({type: "SMP", serverAddress: server, smpErr: {type: "PROXY", proxyErr: {type: "BROKER", brokerErr}}})
|
|
const destination = (proxyErr: object): object => ({type: "PROXY", proxyServer: server, relayServer: relay, proxyErr: {type: "protocolError", protocolErr: {type: "PROXY", proxyErr}}})
|
|
const domain = {nameTLD: T.SimplexTLD.Simplex, domain: "ridgeline", subDomain: []}
|
|
|
|
describe("SimpleX errors", () => {
|
|
const cases: [string, T.ChatError, string][] = [
|
|
["invalid link", {type: "error", errorType: {type: "invalidConnReq"}}, "Invalid connection link: Please check that you used the correct link or ask your contact to send you another one."],
|
|
["unsupported link", {type: "error", errorType: {type: "unsupportedConnReq"}}, "Unsupported connection link: This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link."],
|
|
[
|
|
"name without a valid link",
|
|
{type: "error", errorType: {type: "simplexDomainNotReady", simplexDomain: domain, simplexDomainError: {type: "noValidLink"}}},
|
|
"No valid link: The SimpleX name ridgeline.simplex is registered, but it has no valid link.",
|
|
],
|
|
[
|
|
"name not in a profile",
|
|
{type: "error", errorType: {type: "simplexDomainNotReady", simplexDomain: domain, simplexDomainError: {type: "unknownDomain"}}},
|
|
"Unconfirmed name: The SimpleX name ridgeline.simplex is registered, but not added to profile. Please add it to your address or channel profile, if you are the owner.",
|
|
],
|
|
[
|
|
"file servers not approved",
|
|
{type: "error", errorType: {type: "fileNotApproved", fileId: 7, unknownServers: ["xftp://k@xftp2.example.com", "xftp://k@xftp1.example.com:443", "xftp://j@xftp2.example.com"]}},
|
|
"Unknown servers! Without Tor or VPN, your IP address will be visible to these XFTP relays: xftp1.example.com, xftp2.example.com.",
|
|
],
|
|
["no name servers", agentError({type: "NO_NAME_SERVERS"}), "SimpleX name error: None of your servers are set to resolve SimpleX names. Use a connection link."],
|
|
["name not found", agentError({type: "SMP", serverAddress: server, smpErr: {type: "NAME", nameErr: {type: "NOT_FOUND"}}}), "Name not found: This SimpleX name is not registered. Please check the name."],
|
|
[
|
|
"no name resolver",
|
|
agentError({type: "SMP", serverAddress: server, smpErr: {type: "NAME", nameErr: {type: "NO_RESOLVER"}}}),
|
|
`SimpleX name error: Server ${server} does not support name resolution. Use a connection link.`,
|
|
],
|
|
["resolver error", agentError({type: "SMP", serverAddress: server, smpErr: {type: "NAME", nameErr: {type: "RESOLVER", resolverErr: "bad\nreply"}}}), "SimpleX name error: Resolver error: bad reply"],
|
|
[
|
|
"link removed",
|
|
agentError({type: "SMP", serverAddress: server, smpErr: {type: "AUTH"}}),
|
|
"Connection link removed: Your contact removed this link, or it was a one-time link that was already used. To connect, ask your contact to create a new link.",
|
|
],
|
|
["blocked", agentError({type: "SMP", serverAddress: server, smpErr: {type: "BLOCKED", blockInfo: {reason: T.BlockingReason.Spam}}}), "Connection blocked: Connection is blocked by server operator: Spam."],
|
|
[
|
|
"blocked content",
|
|
agentError({type: "SMP", serverAddress: server, smpErr: {type: "BLOCKED", blockInfo: {reason: T.BlockingReason.Content}}}),
|
|
"Connection blocked: Connection is blocked by server operator: Content violates conditions of use.",
|
|
],
|
|
["quota", agentError({type: "SMP", serverAddress: server, smpErr: {type: "QUOTA"}}), "Undelivered messages: The connection reached the limit of undelivered messages, your contact may be offline."],
|
|
["already connected", agentError({type: "INTERNAL", internalErr: "SEUniqueID"}), "Already connected? It seems like you are already connected via this link. If it is not the case, there was an error (SEUniqueID)."],
|
|
["timeout", agentError(broker({type: "TIMEOUT"})), "Connection timeout: Please check your network connection with smp4.simplex.im and try again."],
|
|
["network", agentError(broker({type: "NETWORK", networkError: {type: "connectError", connectError: "refused"}})), "Connection error: Please check your network connection with smp4.simplex.im and try again."],
|
|
["unknown CA", agentError(broker({type: "NETWORK", networkError: {type: "unknownCAError"}})), "Connection error: Fingerprint in server address does not match certificate: smp4.simplex.im."],
|
|
["host", agentError(broker({type: "HOST"})), "Connection error: Server address is incompatible with network settings: smp4.simplex.im."],
|
|
["server version", agentError(broker({type: "TRANSPORT", transportErr: {type: "version"}})), "Connection error: Server version is incompatible with your app: smp4.simplex.im."],
|
|
["forwarding timeout", agentError(forwarding({type: "TIMEOUT"})), "Private routing error: Error connecting to forwarding server smp4.simplex.im. Please try later."],
|
|
["forwarding network", agentError(forwarding({type: "NETWORK", networkError: {type: "failedError"}})), "Private routing error: Error connecting to forwarding server smp4.simplex.im. Please try later."],
|
|
[
|
|
"forwarding unknown CA",
|
|
agentError(forwarding({type: "NETWORK", networkError: {type: "unknownCAError"}})),
|
|
"Private routing error: Fingerprint in forwarding server address does not match certificate: smp4.simplex.im.",
|
|
],
|
|
["forwarding host", agentError(forwarding({type: "HOST"})), "Private routing error: Forwarding server address is incompatible with network settings: smp4.simplex.im."],
|
|
[
|
|
"forwarding version",
|
|
agentError(forwarding({type: "TRANSPORT", transportErr: {type: "version"}})),
|
|
"Private routing error: Forwarding server version is incompatible with network settings: smp4.simplex.im.",
|
|
],
|
|
[
|
|
"destination timeout",
|
|
agentError(destination({type: "BROKER", brokerErr: {type: "TIMEOUT"}})),
|
|
"Private routing error: Forwarding server smp4.simplex.im failed to connect to destination server smp5.simplex.im. Please try later.",
|
|
],
|
|
[
|
|
"destination network",
|
|
agentError(destination({type: "BROKER", brokerErr: {type: "NETWORK", networkError: {type: "timeoutError"}}})),
|
|
"Private routing error: Forwarding server smp4.simplex.im failed to connect to destination server smp5.simplex.im. Please try later.",
|
|
],
|
|
[
|
|
"no private routing session",
|
|
agentError(destination({type: "NO_SESSION"})),
|
|
"Private routing error: Forwarding server smp4.simplex.im failed to connect to destination server smp5.simplex.im. Please try later.",
|
|
],
|
|
[
|
|
"destination unknown CA",
|
|
agentError(destination({type: "BROKER", brokerErr: {type: "NETWORK", networkError: {type: "unknownCAError"}}})),
|
|
"Private routing error: Fingerprint in destination server address does not match certificate: smp5.simplex.im.",
|
|
],
|
|
[
|
|
"destination host",
|
|
agentError(destination({type: "BROKER", brokerErr: {type: "HOST"}})),
|
|
"Private routing error: Destination server address of smp5.simplex.im is incompatible with forwarding server smp4.simplex.im settings.",
|
|
],
|
|
[
|
|
"destination version",
|
|
agentError(destination({type: "BROKER", brokerErr: {type: "TRANSPORT", transportErr: {type: "version"}}})),
|
|
"Private routing error: Destination server version of smp5.simplex.im is incompatible with forwarding server smp4.simplex.im.",
|
|
],
|
|
["service timeout", agentError({type: "AGENT", agentErr: {type: "A_SERVICE", serviceError: {type: "timeout"}}}), "Connection timeout: The service did not respond. Please try again."],
|
|
["large message", {type: "errorStore", storeError: {type: "largeMsg"}}, "Message is too large! Please reduce the message size and send again."],
|
|
["group not found", {type: "errorStore", storeError: {type: "groupNotFound", groupId: 15}}, "Group not found! This group no longer exists."],
|
|
]
|
|
|
|
it.each(cases)("reads as the apps word it: %s", (_, chatError, text) => {
|
|
expect(chatErrorText(chatError)).toBe(text)
|
|
})
|
|
|
|
it("keeps the chain of error types and their scalar fields for other errors", () => {
|
|
expect(chatErrorText({type: "error", errorType: {type: "groupUserRole", groupInfo: {groupId: 15}, requiredRole: T.GroupMemberRole.Author}} as unknown as T.ChatError)).toBe(
|
|
'SimpleX error: error: groupUserRole: {"requiredRole":"author"}',
|
|
)
|
|
const noService = {...agentError(broker({type: "NO_SERVICE"})), connectionEntity_: {type: "rcvDirectMsgConnection", entityConnection: {connId: 1}}} as T.ChatError
|
|
expect(chatErrorText(noService)).toBe(`SimpleX error: errorAgent: BROKER: NO_SERVICE: {"agentConnId":"","brokerAddress":"${server}"}`)
|
|
expect(chatErrorText(agentError({type: "INTERNAL", internalErr: "other\nline"}))).toBe('SimpleX error: errorAgent: INTERNAL: {"agentConnId":"","internalErr":"other\\nline"}')
|
|
})
|
|
})
|