mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 01:00:18 +02:00
- Cancelling (Change headset, Forget) invalidates the attempt in flight; a connection only becomes the app's once every step finished for it. - A server that stops while the tunnel opens is noticed (exit callbacks are synchronised and replayed), and the app isn't left showing a dead page. - The port is read only once the digits are complete, and range-checked. - Other versions in ~/.cache/frame-control stay unless untouched for 14 days, so a second phone or iPad isn't cut off. - The key is appended on its own line even if authorized_keys lacks a final newline. - The app checks every 20 s, and on returning to the foreground, that the SSH session still answers, and reconnects if not. - The ssh stand-in runs the command as its own process group and passes a TERM on to all of it, so a live-video ffmpeg stops with its stream. - Touch screens show the library's Play buttons (the rule now follows the base one); the failure screen only says it's retrying when it is; the page says the app reconnects rather than naming a desktop menu. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
57 lines
2.7 KiB
Swift
57 lines
2.7 KiB
Swift
import CryptoKit
|
|
import XCTest
|
|
@testable import Frame_Control
|
|
|
|
final class InstallLinkTests: XCTestCase {
|
|
func testAcceptsManifestAndURLLinks() {
|
|
XCTAssertEqual(InstallLink("frame-control://install?manifest=https://example.com/app.json"),
|
|
InstallLink("frame-control://install/?manifest=https://example.com/app.json"))
|
|
XCTAssertEqual(InstallLink("frame-control://install?url=https://example.com/a.apk")?.kind, .url)
|
|
XCTAssertEqual(InstallLink("FRAME-CONTROL://install?manifest=https://example.com/m.json")?.target, "https://example.com/m.json")
|
|
}
|
|
|
|
func testRejectsAnythingElse() {
|
|
for raw in ["frame-control://other?url=https://example.com/a.apk",
|
|
"frame-control://install?url=ftp://example.com/a.apk",
|
|
"frame-control://install?url=https://user:pw@example.com/a.apk",
|
|
"frame-control://install?url=https://example.com/a&manifest=https://example.com/b",
|
|
"frame-control://install?url=https://a.example/x&url=https://b.example/y",
|
|
"frame-control://install?url=",
|
|
"frame-control://install/deeper?url=https://example.com/a.apk",
|
|
"https://example.com/?url=https://example.com/a.apk",
|
|
"frame-control://install?url=https://example.com/" + String(repeating: "a", count: 2100)] {
|
|
XCTAssertNil(InstallLink(raw), raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
final class HeadsetServerTests: XCTestCase {
|
|
func testReadsThePortTheServerPrints() {
|
|
XCTAssertEqual(HeadsetServer.port(in: "Frame Control on http://127.0.0.1:41234 (alias: frame; Ctrl-C to stop)\n"), 41234)
|
|
XCTAssertNil(HeadsetServer.port(in: "Traceback (most recent call last):"))
|
|
XCTAssertNil(HeadsetServer.port(in: "Frame Control on http://127.0.0.1:4")) // more digits may follow
|
|
XCTAssertNil(HeadsetServer.port(in: "Frame Control on http://127.0.0.1:99999 "))
|
|
}
|
|
|
|
func testBundleIsInTheApp() throws {
|
|
let bundle = try HeadsetServer.Bundle.fromApp()
|
|
XCTAssertGreaterThan(bundle.data.count, 100_000)
|
|
XCTAssertEqual(bundle.version.count, 16)
|
|
}
|
|
}
|
|
|
|
final class KeyTests: XCTestCase {
|
|
func testAuthorizedKeysLine() {
|
|
let line = DeviceKey.authorizedKeysLine(Curve25519.Signing.PrivateKey(), comment: "frame-control@iPhone")
|
|
let parts = line.split(separator: " ")
|
|
XCTAssertEqual(parts.count, 3)
|
|
XCTAssertEqual(parts[0], "ssh-ed25519")
|
|
XCTAssertEqual(Data(base64Encoded: String(parts[1]))?.count, 51) // string "ssh-ed25519" + 32-byte key
|
|
XCTAssertEqual(parts[2], "frame-control@iPhone")
|
|
}
|
|
|
|
func testShellQuote() {
|
|
XCTAssertEqual(shellQuote("it's"), "'it'\\''s'")
|
|
}
|
|
}
|