@@ -1829,6 +1829,71 @@ describe("doctor legacy state migrations", () => {
|
1829 | 1829 | }); |
1830 | 1830 | }); |
1831 | 1831 | |
| 1832 | +it("archives exact legacy npm install record when SQLite has authoritative resolved metadata", async () => { |
| 1833 | +const root = await makeTempRoot(); |
| 1834 | +await writeExistingPluginInstallIndex(root, { |
| 1835 | +discord: { |
| 1836 | +source: "npm", |
| 1837 | +spec: "@openclaw/discord@latest", |
| 1838 | +resolvedName: "@openclaw/discord", |
| 1839 | +resolvedVersion: "2026.6.16", |
| 1840 | +integrity: "sha512-current", |
| 1841 | +installedAt: "2026-06-16T12:00:00.000Z", |
| 1842 | +}, |
| 1843 | +}); |
| 1844 | +const sourcePath = writeLegacyPluginInstallIndex(root, { |
| 1845 | +discord: { |
| 1846 | +source: "npm", |
| 1847 | +spec: "@openclaw/discord@2026.6.16", |
| 1848 | +version: "2026.6.16", |
| 1849 | +installedAt: "2026-06-01T12:00:00.000Z", |
| 1850 | +}, |
| 1851 | +}); |
| 1852 | + |
| 1853 | +const result = await runLegacyStateMigrationsForRoot(root); |
| 1854 | + |
| 1855 | +expect(result.warnings).toStrictEqual([]); |
| 1856 | +expect(fs.existsSync(sourcePath)).toBe(false); |
| 1857 | +expect(fs.existsSync(`${sourcePath}.migrated`)).toBe(true); |
| 1858 | +await expect(readPersistedInstalledPluginIndex({ stateDir: root })).resolves.toMatchObject({ |
| 1859 | +installRecords: { |
| 1860 | +discord: { |
| 1861 | +source: "npm", |
| 1862 | +spec: "@openclaw/discord@latest", |
| 1863 | +resolvedName: "@openclaw/discord", |
| 1864 | +resolvedVersion: "2026.6.16", |
| 1865 | +integrity: "sha512-current", |
| 1866 | +}, |
| 1867 | +}, |
| 1868 | +}); |
| 1869 | +}); |
| 1870 | + |
| 1871 | +it("keeps exact legacy npm install record when SQLite lacks authoritative package identity", async () => { |
| 1872 | +const root = await makeTempRoot(); |
| 1873 | +await writeExistingPluginInstallIndex(root, { |
| 1874 | +demo: { |
| 1875 | +source: "npm", |
| 1876 | +spec: "demo@latest", |
| 1877 | +version: "1.0.0", |
| 1878 | +}, |
| 1879 | +}); |
| 1880 | +const sourcePath = writeLegacyPluginInstallIndex(root, { |
| 1881 | +demo: { |
| 1882 | +source: "npm", |
| 1883 | +spec: "demo@1.0.0", |
| 1884 | +version: "1.0.0", |
| 1885 | +}, |
| 1886 | +}); |
| 1887 | + |
| 1888 | +const result = await runLegacyStateMigrationsForRoot(root); |
| 1889 | + |
| 1890 | +expect(result.warnings).toStrictEqual([ |
| 1891 | +"Left plugin install index in place because shared SQLite state has conflicting plugin install metadata for: demo", |
| 1892 | +]); |
| 1893 | +expect(fs.existsSync(sourcePath)).toBe(true); |
| 1894 | +expect(fs.existsSync(`${sourcePath}.migrated`)).toBe(false); |
| 1895 | +}); |
| 1896 | + |
1832 | 1897 | for (const fixture of [ |
1833 | 1898 | { |
1834 | 1899 | label: "name different packages", |
|