1+ import { aes , stringUtils } from '@internxt/lib' ;
2+ import { logger } from '@internxt/drive-desktop-core/build/backend' ;
3+ import { validateMnemonic } from 'bip39' ;
4+ import { clipboard } from 'electron' ;
5+ import { DriveServerError } from '../../../../infra/drive-server/drive-server.error' ;
6+ import * as fetchFileMetaByPathModule from '../../../../infra/drive-server/services/files/services/fetch-file-meta-by-path' ;
7+ import * as fetchFolderMetaByPathModule from '../../../../infra/drive-server/services/folder/services/fetch-folder-meta-by-path' ;
8+ import * as createSharingModule from '../../../../infra/drive-server/services/sharings/services/create-sharing' ;
9+ import * as fetchPublicSharingDomainsModule from '../../../../infra/drive-server/services/sharings/services/fetch-public-sharing-domains' ;
10+ import * as getCredentialsModule from '../../auth/get-credentials' ;
11+ import { generateLink } from './generate-link' ;
12+ import { call , calls , partialSpyOn } from 'tests/vitest/utils.helper' ;
13+
14+ vi . mock ( 'bip39' , ( ) => ( {
15+ validateMnemonic : vi . fn ( ) ,
16+ } ) ) ;
17+
18+ describe ( 'generate-link' , ( ) => {
19+ const getCredentialsMock = partialSpyOn ( getCredentialsModule , 'getCredentials' ) ;
20+ const fetchFileMetaByPathMock = partialSpyOn ( fetchFileMetaByPathModule , 'fetchFileMetaByPath' ) ;
21+ const fetchFolderMetaByPathMock = partialSpyOn ( fetchFolderMetaByPathModule , 'fetchFolderMetaByPath' ) ;
22+ const fetchPublicSharingDomainsMock = partialSpyOn ( fetchPublicSharingDomainsModule , 'fetchPublicSharingDomains' ) ;
23+ const createSharingMock = partialSpyOn ( createSharingModule , 'createSharing' ) ;
24+ const loggerDebugMock = partialSpyOn ( logger , 'debug' ) ;
25+ const clipboardWriteTextMock = partialSpyOn ( clipboard , 'writeText' ) ;
26+
27+ const validateMnemonicMock = vi . mocked ( validateMnemonic ) ;
28+ const encryptMock = partialSpyOn ( aes , 'encrypt' ) ;
29+ const decryptMock = partialSpyOn ( aes , 'decrypt' ) ;
30+ const generateRandomStringUrlSafeMock = partialSpyOn ( stringUtils , 'generateRandomStringUrlSafe' ) ;
31+ const encodeV4UuidMock = partialSpyOn ( stringUtils , 'encodeV4Uuid' ) ;
32+
33+ beforeEach ( ( ) => {
34+ getCredentialsMock . mockReturnValue ( { newToken : 'token' , mnemonic : 'valid mnemonic' } ) ;
35+ validateMnemonicMock . mockReturnValue ( true ) ;
36+ generateRandomStringUrlSafeMock . mockReturnValue ( 'plain-code' ) ;
37+ encryptMock
38+ . mockReturnValueOnce ( 'encrypted-mnemonic' )
39+ . mockReturnValueOnce ( 'encrypted-code' ) ;
40+ decryptMock . mockReturnValue ( 'recovered-code' ) ;
41+ encodeV4UuidMock . mockReturnValue ( 'encoded-sharing-id' ) ;
42+ } ) ;
43+
44+ it ( 'should create a public sharing link for a file path and copy it to clipboard' , async ( ) => {
45+ fetchFileMetaByPathMock . mockResolvedValueOnce ( { data : { uuid : 'file-uuid' } } as object ) ;
46+ fetchPublicSharingDomainsMock . mockResolvedValueOnce ( { data : { list : [ 'https://share.internxt.test' ] } } as object ) ;
47+ createSharingMock . mockResolvedValueOnce ( {
48+ data : {
49+ encryptedCode : 'server-encrypted-code' ,
50+ id : '08dfcc54-16d6-4f9d-9124-8a36b12e07dd' ,
51+ } ,
52+ } as object ) ;
53+
54+ const result = await generateLink ( { path : '/folder/file.txt' } ) ;
55+
56+ expect ( result ) . toBe ( 'https://share.internxt.test/sh/file/encoded-sharing-id/recovered-code' ) ;
57+ call ( fetchFileMetaByPathMock ) . toStrictEqual ( { path : '/folder/file.txt' } ) ;
58+ call ( fetchPublicSharingDomainsMock ) . toStrictEqual ( [ ] ) ;
59+ call ( createSharingMock ) . toStrictEqual ( {
60+ body : {
61+ encryptedCode : 'encrypted-code' ,
62+ encryptedPassword : null ,
63+ encryptionAlgorithm : 'inxt-v2' ,
64+ encryptionKey : 'encrypted-mnemonic' ,
65+ itemId : 'file-uuid' ,
66+ itemType : 'file' ,
67+ persistPreviousSharing : true ,
68+ } ,
69+ } ) ;
70+ call ( clipboardWriteTextMock ) . toBe ( 'https://share.internxt.test/sh/file/encoded-sharing-id/recovered-code' ) ;
71+ call ( loggerDebugMock ) . toMatchObject ( {
72+ msg : 'link copied' ,
73+ itemType : 'file' ,
74+ path : '/folder/file.txt' ,
75+ } ) ;
76+ } ) ;
77+
78+ it ( 'should fallback to folder metadata when file metadata is not found' , async ( ) => {
79+ fetchFileMetaByPathMock . mockResolvedValueOnce ( { error : new DriveServerError ( 'NOT_FOUND' , 404 ) } as object ) ;
80+ fetchFolderMetaByPathMock . mockResolvedValueOnce ( { data : { uuid : 'folder-uuid' } } as object ) ;
81+ fetchPublicSharingDomainsMock . mockResolvedValueOnce ( { data : [ 'https://share.internxt.test' ] } as object ) ;
82+ createSharingMock . mockResolvedValueOnce ( {
83+ data : {
84+ encryptedCode : 'server-encrypted-code' ,
85+ id : '08dfcc54-16d6-4f9d-9124-8a36b12e07dd' ,
86+ } ,
87+ } as object ) ;
88+
89+ const result = await generateLink ( { path : '/folder' } ) ;
90+
91+ expect ( result ) . toBe ( 'https://share.internxt.test/sh/folder/encoded-sharing-id/recovered-code' ) ;
92+ call ( fetchFileMetaByPathMock ) . toStrictEqual ( { path : '/folder' } ) ;
93+ call ( fetchFolderMetaByPathMock ) . toStrictEqual ( { path : '/folder' } ) ;
94+ } ) ;
95+
96+ it ( 'should fail before any network call when mnemonic is invalid' , async ( ) => {
97+ validateMnemonicMock . mockReturnValue ( false ) ;
98+
99+ await expect ( generateLink ( { path : '/folder/file.txt' } ) ) . rejects . toThrow ( 'The user mnemonic is invalid' ) ;
100+
101+ calls ( fetchFileMetaByPathMock ) . toHaveLength ( 0 ) ;
102+ calls ( fetchFolderMetaByPathMock ) . toHaveLength ( 0 ) ;
103+ calls ( fetchPublicSharingDomainsMock ) . toHaveLength ( 0 ) ;
104+ calls ( createSharingMock ) . toHaveLength ( 0 ) ;
105+ calls ( clipboardWriteTextMock ) . toHaveLength ( 0 ) ;
106+ } ) ;
107+ } ) ;
0 commit comments