From 2525f7146b5f54812995f859708082b1f4f9d0f9 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Wed, 17 Aug 2022 17:51:59 +0200
Subject: [PATCH] fix(presets): handle null values from azure (#17236)

---
 lib/config/presets/local/common.spec.ts  | 48 ++++++++++++++++++++++++
 lib/config/presets/local/common.ts       |  8 ++--
 lib/modules/platform/azure/index.spec.ts | 20 +++++++++-
 lib/modules/platform/azure/index.ts      | 10 +++--
 4 files changed, 77 insertions(+), 9 deletions(-)
 create mode 100644 lib/config/presets/local/common.spec.ts

diff --git a/lib/config/presets/local/common.spec.ts b/lib/config/presets/local/common.spec.ts
new file mode 100644
index 0000000000..6bf9872b10
--- /dev/null
+++ b/lib/config/presets/local/common.spec.ts
@@ -0,0 +1,48 @@
+import { platform } from '../../../../test/util';
+import { ExternalHostError } from '../../../types/errors/external-host-error';
+import { PRESET_DEP_NOT_FOUND } from '../util';
+import { fetchJSONFile, getPresetFromEndpoint } from './common';
+
+describe('config/presets/local/common', () => {
+  describe('fetchJSONFile', () => {
+    it('throws for null', async () => {
+      platform.getRawFile.mockResolvedValueOnce(null);
+
+      await expect(fetchJSONFile('some/repo', 'default.json')).rejects.toThrow(
+        PRESET_DEP_NOT_FOUND
+      );
+    });
+
+    it('throws for ExternalHostError', async () => {
+      platform.getRawFile.mockRejectedValueOnce(
+        new ExternalHostError(new Error())
+      );
+
+      await expect(fetchJSONFile('some/repo', 'default.json')).rejects.toThrow(
+        ExternalHostError
+      );
+    });
+
+    it('throws for Error', async () => {
+      platform.getRawFile.mockRejectedValueOnce(new Error());
+
+      await expect(fetchJSONFile('some/repo', 'default.json')).rejects.toThrow(
+        PRESET_DEP_NOT_FOUND
+      );
+    });
+  });
+
+  describe('getPresetFromEndpoint', () => {
+    it('works', async () => {
+      platform.getRawFile.mockResolvedValueOnce('{}');
+      expect(
+        await getPresetFromEndpoint(
+          'some/repo',
+          'default.json',
+          undefined,
+          'dummy'
+        )
+      ).toEqual({});
+    });
+  });
+});
diff --git a/lib/config/presets/local/common.ts b/lib/config/presets/local/common.ts
index 3271595554..7648fdd56f 100644
--- a/lib/config/presets/local/common.ts
+++ b/lib/config/presets/local/common.ts
@@ -13,7 +13,6 @@ export async function fetchJSONFile(
   try {
     raw = await platform.getRawFile(fileName, repo);
   } catch (err) {
-    // istanbul ignore if: not testable with nock
     if (err instanceof ExternalHostError) {
       throw err;
     }
@@ -26,8 +25,11 @@ export async function fetchJSONFile(
     throw new Error(PRESET_DEP_NOT_FOUND);
   }
 
-  // TODO: null check #7154
-  return parsePreset(raw!);
+  if (!raw) {
+    throw new Error(PRESET_DEP_NOT_FOUND);
+  }
+
+  return parsePreset(raw);
 }
 
 export function getPresetFromEndpoint(
diff --git a/lib/modules/platform/azure/index.spec.ts b/lib/modules/platform/azure/index.spec.ts
index e6a881cdb5..b02867336a 100644
--- a/lib/modules/platform/azure/index.spec.ts
+++ b/lib/modules/platform/azure/index.spec.ts
@@ -1,10 +1,12 @@
 import { Readable } from 'stream';
 import is from '@sindresorhus/is';
+import type { IGitApi } from 'azure-devops-node-api/GitApi';
 import {
   GitPullRequestMergeStrategy,
   GitStatusState,
   PullRequestStatus,
 } from 'azure-devops-node-api/interfaces/GitInterfaces.js';
+import { partial } from '../../../../test/util';
 import {
   REPOSITORY_ARCHIVED,
   REPOSITORY_NOT_FOUND,
@@ -167,13 +169,13 @@ describe('modules/platform/azure/index', () => {
     if (is.string(args)) {
       return azure.initRepo({
         repository: args,
-      } as any);
+      });
     }
 
     return azure.initRepo({
       repository: 'some/repo',
       ...args,
-    } as any);
+    });
   }
 
   describe('initRepo', () => {
@@ -1308,6 +1310,10 @@ describe('modules/platform/azure/index', () => {
   });
 
   describe('getJsonFile()', () => {
+    beforeEach(async () => {
+      await initRepo();
+    });
+
     it('returns file content', async () => {
       const data = { foo: 'bar' };
       azureApi.gitApi.mockImplementationOnce(
@@ -1396,5 +1402,15 @@ describe('modules/platform/azure/index', () => {
       expect(res).toEqual(data);
       expect(gitApiMock.getItemContent.mock.calls).toMatchSnapshot();
     });
+
+    it('returns null', async () => {
+      azureApi.gitApi.mockResolvedValueOnce(
+        partial<IGitApi>({
+          getRepositories: jest.fn(() => Promise.resolve([])),
+        })
+      );
+      const res = await azure.getJsonFile('file.json', 'foo/bar');
+      expect(res).toBeNull();
+    });
   });
 });
diff --git a/lib/modules/platform/azure/index.ts b/lib/modules/platform/azure/index.ts
index 223288f16f..3259fc0748 100644
--- a/lib/modules/platform/azure/index.ts
+++ b/lib/modules/platform/azure/index.ts
@@ -134,13 +134,16 @@ export async function getRawFile(
     repoId = config.repoId;
   }
 
+  if (!repoId) {
+    return null;
+  }
+
   const versionDescriptor: GitVersionDescriptor = {
     version: branchOrTag,
   } as GitVersionDescriptor;
 
   const buf = await azureApiGit.getItemContent(
-    // TODO #7154
-    repoId!,
+    repoId,
     fileName,
     undefined,
     undefined,
@@ -161,8 +164,7 @@ export async function getJsonFile(
   branchOrTag?: string
 ): Promise<any | null> {
   const raw = await getRawFile(fileName, repoName, branchOrTag);
-  // TODO #7154
-  return JSON5.parse(raw!);
+  return raw ? JSON5.parse(raw) : null;
 }
 
 export async function initRepo({
-- 
GitLab