1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { describe, it } from 'mocha';
17import { expect } from 'chai';
18import sinon from 'sinon';
19import proxyquire from 'proxyquire';
20import { constants } from 'fs';
21
22import { IOptions } from '../../../src/configs/IOptions';
23
24const fsStub = {
25  existsSync: sinon.stub(),
26  accessSync: sinon.stub(),
27};
28
29const fsExtraStub = {
30  readJsonSync: sinon.stub()
31};
32
33const { FileUtils, IOptions } = proxyquire('../../../src/utils/FileUtils', {
34  'fs': fsStub,
35  'fs-extra': fsExtraStub
36});
37
38describe('FileUtils.readFileAsJson', () => {
39  let consoleErrorStub:sinon.SinonStub;
40  beforeEach(() => {
41    consoleErrorStub = sinon.stub(console, 'error');
42  });
43
44  afterEach(() => {
45    sinon.restore();
46  });
47
48  it('should return undefined if file does not exist', () => {
49    fsStub.existsSync.returns(false);
50    const result = FileUtils.readFileAsJson('fakePath.json');
51    expect(result).to.be.undefined;
52    expect(consoleErrorStub.calledWith('File <fakePath.json> is not found.')).to.be.true;
53  });
54
55  it('should return undefined if readJsonSync throws an error', () => {
56    fsStub.existsSync.returns(true);
57    fsExtraStub.readJsonSync.throws(new Error('read error'));
58    const result = FileUtils.readFileAsJson('fakePath.json');
59    expect(result).to.be.undefined;
60    expect(consoleErrorStub.calledWith('json file read error: fakePath.json')).to.be.true;
61  });
62
63  it('should return JSON content if file exists and is valid', () => {
64    const jsonContent: IOptions = { mCompact: true };
65    fsStub.existsSync.returns(true);
66    fsExtraStub.readJsonSync.returns(jsonContent);
67    const result = FileUtils.readFileAsJson('validPath.json');
68    expect(result).to.deep.equal(jsonContent);
69  });
70});
71
72describe('FileUtils.getFileExtension', () => {
73  it('should return undefined for empty string', () => {
74    const result = FileUtils.getFileExtension('');
75    expect(result).to.be.undefined;
76  });
77
78  it('should return undefined for string without dot', () => {
79    const result = FileUtils.getFileExtension('filename');
80    expect(result).to.be.undefined;
81  });
82
83  it('should return undefined for path string without dot', () => {
84    const result = FileUtils.getFileExtension('path/to.test/filename');
85    expect(result).to.be.undefined;
86  });
87
88  it('should return undefined for string ending with a dot', () => {
89    const result = FileUtils.getFileExtension('filename.');
90    expect(result).to.equal('');
91  });
92
93  it('should return extension for valid file path', () => {
94    const result = FileUtils.getFileExtension('/path/to/file.txt');
95    expect(result).to.equal('txt');
96  });
97
98  it('should return extension for valid file name with multiple dots', () => {
99    const result = FileUtils.getFileExtension('/path/to/file.name.with.dots.txt');
100    expect(result).to.equal('txt');
101  });
102});
103
104describe('FileUtils.isRelativePath', () => {
105  it('should return true for relative paths starting with ./', () => {
106    const result = FileUtils.isRelativePath('./file.txt');
107    expect(result).to.be.true;
108  });
109
110  it('should return true for relative paths starting with ../', () => {
111    const result = FileUtils.isRelativePath('../file.txt');
112    expect(result).to.be.true;
113  });
114
115  it('should return true for relative paths starting with .\\', () => {
116    const result = FileUtils.isRelativePath('.\\file.txt');
117    expect(result).to.be.true;
118  });
119
120  it('should return true for relative paths starting with ..\\', () => {
121    const result = FileUtils.isRelativePath('..\\file.txt');
122    expect(result).to.be.true;
123  });
124
125  it('should return false for absolute paths', () => {
126    const result = FileUtils.isRelativePath('/absolute/path/to/file.txt');
127    expect(result).to.be.false;
128  });
129
130  it('should return false for Windows absolute paths', () => {
131    const result = FileUtils.isRelativePath('C:\\absolute\\path\\to\\file.txt');
132    expect(result).to.be.false;
133  });
134});
135
136describe('FileUtils.isReadableFile', () => {
137  afterEach(() => {
138    sinon.restore();
139  });
140
141  it('should return true if file is readable', () => {
142    fsStub.accessSync.withArgs('readableFile.txt', constants.R_OK).returns(undefined);
143    const result = FileUtils.isReadableFile('readableFile.txt');
144    expect(result).to.be.true;
145  });
146
147  it('should return false if file is not readable', () => {
148    fsStub.accessSync.withArgs('unreadableFile.txt', constants.R_OK).throws(new Error('Not readable'));
149    const result = FileUtils.isReadableFile('unreadableFile.txt');
150    expect(result).to.be.false;
151  });
152});
153
154describe('FileUtils.toUnixPath', () => {
155  let osStub: sinon.SinonStub;
156  let pathStub: sinon.SinonStub;
157
158  beforeEach(() => {
159    osStub = sinon.stub(require('os'), 'platform');
160    pathStub = sinon.stub(require('path'), 'sep').value('/'); // Default to Unix-like separator
161  });
162
163  afterEach(() => {
164    sinon.restore();
165  });
166
167  it('should convert Windows path to Unix path', () => {
168    osStub.returns('win32');
169    const proxyquireUtils = proxyquire('../../../src/utils/FileUtils', {
170      'os': {
171        platform: osStub
172      },
173      'path': {
174        sep: '\\',
175        posix: {
176          join: (...paths: string[]) => paths.join('/')
177        }
178      }
179    });
180    const result = proxyquireUtils.FileUtils.toUnixPath('C:\\path\\to\\file.txt');
181    expect(result).to.equal('C:/path/to/file.txt');
182  });
183
184  it('should return Unix path unchanged', () => {
185    osStub.returns('linux');
186    const result = FileUtils.toUnixPath('/path/to/file.txt');
187    expect(result).to.equal('/path/to/file.txt');
188  });
189});
190
191describe('FileUtils.getAbsPathBaseConfigPath', () => {
192  it('should return absolute path', () => {
193    expect(FileUtils.getAbsPathBaseConfigPath('/abs/path/abc', 'file.txt')).to.equal('/abs/path/file.txt')
194  });
195
196  // Return non-absolute path? May need to be fixed.
197  it('should return non-absolute path', () => {
198    expect(FileUtils.getAbsPathBaseConfigPath('not_abs/path/abc', 'file.txt')).to.equal('not_abs/path/file.txt')
199  });
200
201  it('should handle empty paths', () => {
202    expect(FileUtils.getAbsPathBaseConfigPath('', '')).to.equal('.')
203  });
204});