diff --git a/commands/function/deploy.ts b/commands/function/deploy.ts
index 11c2f1f92..f2afea049 100644
--- a/commands/function/deploy.ts
+++ b/commands/function/deploy.ts
@@ -61,7 +61,9 @@ exports.handler = async options => {
       derivedAccountId,
       functionPath
     );
-    const successResp = await poll(getBuildStatus, derivedAccountId, buildId);
+    const successResp = await poll(() =>
+      getBuildStatus(derivedAccountId, buildId)
+    );
     const buildTimeSeconds = (successResp.buildTime / 1000).toFixed(2);
 
     SpinniesManager.succeed('loading');
diff --git a/commands/project/cloneApp.ts b/commands/project/cloneApp.ts
index 5a8ec9535..8a027dd3a 100644
--- a/commands/project/cloneApp.ts
+++ b/commands/project/cloneApp.ts
@@ -104,7 +104,9 @@ exports.handler = async options => {
     const {
       data: { exportId },
     } = await cloneApp(derivedAccountId, appId);
-    const { status } = await poll(checkCloneStatus, derivedAccountId, exportId);
+    const { status } = await poll(() =>
+      checkCloneStatus(derivedAccountId, exportId)
+    );
     if (status === 'SUCCESS') {
       // Ensure correct project folder structure exists
       const baseDestPath = path.resolve(getCwd(), projectDest);
diff --git a/commands/project/migrateApp.ts b/commands/project/migrateApp.ts
index 85b893d18..1380a5878 100644
--- a/commands/project/migrateApp.ts
+++ b/commands/project/migrateApp.ts
@@ -189,7 +189,9 @@ exports.handler = async options => {
       projectName
     );
     const { id } = migrateResponse;
-    const pollResponse = await poll(checkMigrationStatus, derivedAccountId, id);
+    const pollResponse = await poll(() =>
+      checkMigrationStatus(derivedAccountId, id)
+    );
     const { status, project } = pollResponse;
     if (status === 'SUCCESS') {
       const absoluteDestPath = path.resolve(getCwd(), projectDest);
diff --git a/lang/en.lyaml b/lang/en.lyaml
index 7f07353a8..0ba9cc830 100644
--- a/lang/en.lyaml
+++ b/lang/en.lyaml
@@ -1113,7 +1113,6 @@ en:
         notFound: "Your project {{#bold}}{{ projectName }}{{/bold}} could not be found in {{#bold}}{{ accountIdentifier }}{{/bold}}."
       pollFetchProject:
         checkingProject: "Checking if project exists in {{ accountIdentifier }}"
-        unableToFindAutodeployStatus: "Unable to find the auto deploy for build #{{ buildId }}. This deploy may have been skipped. {{ viewDeploysLink }}."
       logFeedbackMessage:
         feedbackHeader: "We'd love to hear your feedback!"
         feedbackMessage: "How are you liking the new projects and developer tools? \n > Run `{{#yellow}}hs feedback{{/yellow}}` to let us know what you think!\n"
@@ -1129,6 +1128,7 @@ en:
         buildSucceededAutomaticallyDeploying: "Build #{{ buildId }} succeeded. {{#bold}}Automatically deploying{{/bold}} to {{ accountIdentifier }}\n"
         cleanedUpTempFile: "Cleaned up temporary file {{ path }}"
         viewDeploys: "View all deploys for this project in HubSpot"
+        unableToFindAutodeployStatus: "Unable to find the auto deploy for build #{{ buildId }}. This deploy may have been skipped. {{ viewDeploysLink }}."
     projectUpload:
       uploadProjectFiles:
         add: "Uploading {{#bold}}{{ projectName }}{{/bold}} project files to {{ accountIdentifier }}"
diff --git a/lib/__tests__/oauth.test.ts b/lib/__tests__/oauth.test.ts
new file mode 100644
index 000000000..2de71816e
--- /dev/null
+++ b/lib/__tests__/oauth.test.ts
@@ -0,0 +1,142 @@
+import express, { Request, Response } from 'express';
+import open from 'open';
+import { OAuth2Manager } from '@hubspot/local-dev-lib/models/OAuth2Manager';
+import { getAccountConfig } from '@hubspot/local-dev-lib/config';
+import { addOauthToAccountConfig } from '@hubspot/local-dev-lib/oauth';
+import { logger } from '@hubspot/local-dev-lib/logger';
+import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
+import { DEFAULT_OAUTH_SCOPES } from '@hubspot/local-dev-lib/constants/auth';
+import { authenticateWithOauth } from '../oauth';
+
+jest.mock('express');
+jest.mock('open');
+jest.mock('@hubspot/local-dev-lib/models/OAuth2Manager');
+jest.mock('@hubspot/local-dev-lib/config');
+jest.mock('@hubspot/local-dev-lib/oauth');
+jest.mock('@hubspot/local-dev-lib/logger');
+
+const mockedExpress = express as unknown as jest.Mock;
+const mockedOAuth2Manager = OAuth2Manager as unknown as jest.Mock;
+const mockedGetAccountConfig = getAccountConfig as jest.Mock;
+
+describe('lib/oauth', () => {
+  const mockExpressReq = {
+    query: { code: 'test-auth-code' },
+  } as unknown as Request;
+  const mockExpressResp = { send: jest.fn() } as unknown as Response;
+
+  const mockAccountConfig = {
+    accountId: 123,
+    clientId: 'test-client-id',
+    clientSecret: 'test-client-secret',
+    scopes: ['test-scope'],
+    env: ENVIRONMENTS.PROD,
+  };
+
+  beforeEach(() => {
+    mockedExpress.mockReturnValue({
+      get: jest.fn().mockImplementation((path, callback) => {
+        if (path === '/oauth-callback') {
+          callback(mockExpressReq, mockExpressResp);
+        }
+      }),
+      listen: jest.fn().mockReturnValue({ close: jest.fn() }),
+    });
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
+  describe('authenticateWithOauth()', () => {
+    it('should setup OAuth and authenticate successfully', async () => {
+      // Mock successful OAuth flow
+      const mockOAuth2Manager = {
+        account: mockAccountConfig,
+        exchangeForTokens: jest.fn().mockResolvedValue({}),
+      };
+
+      mockedOAuth2Manager.mockImplementation(() => mockOAuth2Manager);
+      mockedGetAccountConfig.mockReturnValue({
+        env: ENVIRONMENTS.PROD,
+      });
+
+      await authenticateWithOauth(mockAccountConfig);
+
+      // Verify OAuth2Manager was initialized correctly
+      expect(mockedOAuth2Manager).toHaveBeenCalledWith({
+        ...mockAccountConfig,
+        env: ENVIRONMENTS.PROD,
+      });
+
+      // Verify logger was called
+      expect(logger.log).toHaveBeenCalledWith('Authorizing');
+
+      // Verify OAuth tokens were added to config
+      expect(addOauthToAccountConfig).toHaveBeenCalledWith(mockOAuth2Manager);
+    });
+
+    it('should handle missing clientId', async () => {
+      const invalidConfig = {
+        ...mockAccountConfig,
+        clientId: undefined,
+      };
+
+      mockedOAuth2Manager.mockImplementation(() => ({
+        account: invalidConfig,
+      }));
+
+      const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
+        throw new Error('exit');
+      });
+
+      await expect(authenticateWithOauth(invalidConfig)).rejects.toThrow(
+        'exit'
+      );
+      expect(logger.error).toHaveBeenCalled();
+      expect(exitSpy).toHaveBeenCalled();
+      exitSpy.mockRestore();
+    });
+
+    it('should use default scopes when none provided', async () => {
+      const configWithoutScopes = {
+        ...mockAccountConfig,
+        scopes: undefined,
+      };
+
+      const mockOAuth2Manager = {
+        account: configWithoutScopes,
+        exchangeForTokens: jest.fn().mockResolvedValue({}),
+      };
+
+      mockedOAuth2Manager.mockImplementation(() => mockOAuth2Manager);
+
+      await authenticateWithOauth(configWithoutScopes);
+
+      // Verify default scopes were used
+      expect(open).toHaveBeenCalledWith(
+        expect.stringContaining(
+          encodeURIComponent(DEFAULT_OAUTH_SCOPES.join(' '))
+        ),
+        expect.anything()
+      );
+    });
+
+    it('should handle OAuth exchange failure', async () => {
+      const mockOAuth2Manager = {
+        account: mockAccountConfig,
+        exchangeForTokens: jest
+          .fn()
+          .mockRejectedValue(new Error('Exchange failed')),
+      };
+
+      mockedOAuth2Manager.mockImplementation(() => mockOAuth2Manager);
+
+      await authenticateWithOauth(mockAccountConfig);
+
+      expect(mockExpressResp.send).toHaveBeenCalledWith(
+        expect.stringContaining('Authorization failed')
+      );
+    });
+  });
+});
diff --git a/lib/__tests__/polling.test.ts b/lib/__tests__/polling.test.ts
new file mode 100644
index 000000000..45afc1a68
--- /dev/null
+++ b/lib/__tests__/polling.test.ts
@@ -0,0 +1,107 @@
+import { poll, DEFAULT_POLLING_STATES } from '../polling';
+import { DEFAULT_POLLING_DELAY } from '../constants';
+import { HubSpotPromise } from '@hubspot/local-dev-lib/types/Http';
+
+// Mock response types
+type MockResponse = {
+  status: string;
+};
+
+// Helper to create a mock polling callback
+const createMockCallback = (responses: MockResponse[]) => {
+  let callCount = 0;
+  return jest.fn((): HubSpotPromise<{ status: string }> => {
+    const response = responses[callCount];
+    callCount++;
+    return Promise.resolve({ data: response }) as HubSpotPromise<{
+      status: string;
+    }>;
+  });
+};
+
+describe('lib/polling', () => {
+  beforeEach(() => {
+    jest.useFakeTimers();
+  });
+
+  afterEach(() => {
+    jest.useRealTimers();
+  });
+
+  describe('poll()', () => {
+    it('should resolve when status is SUCCESS', async () => {
+      const mockCallback = createMockCallback([
+        { status: DEFAULT_POLLING_STATES.STARTED },
+        { status: DEFAULT_POLLING_STATES.SUCCESS },
+      ]);
+
+      const pollPromise = poll(mockCallback);
+
+      // Fast-forward through two polling intervals
+      jest.advanceTimersByTime(DEFAULT_POLLING_DELAY * 2);
+
+      const result = await pollPromise;
+      expect(result.status).toBe(DEFAULT_POLLING_STATES.SUCCESS);
+      expect(mockCallback).toHaveBeenCalledTimes(2);
+    });
+
+    it('should reject when status is ERROR', async () => {
+      const mockCallback = createMockCallback([
+        { status: DEFAULT_POLLING_STATES.STARTED },
+        { status: DEFAULT_POLLING_STATES.ERROR },
+      ]);
+
+      const pollPromise = poll(mockCallback);
+
+      jest.advanceTimersByTime(DEFAULT_POLLING_DELAY * 2);
+
+      await expect(pollPromise).rejects.toEqual({
+        status: DEFAULT_POLLING_STATES.ERROR,
+      });
+      expect(mockCallback).toHaveBeenCalledTimes(2);
+    });
+
+    it('should reject when status is FAILURE', async () => {
+      const mockCallback = createMockCallback([
+        { status: DEFAULT_POLLING_STATES.STARTED },
+        { status: DEFAULT_POLLING_STATES.FAILURE },
+      ]);
+
+      const pollPromise = poll(mockCallback);
+
+      jest.advanceTimersByTime(DEFAULT_POLLING_DELAY * 2);
+
+      await expect(pollPromise).rejects.toEqual({
+        status: DEFAULT_POLLING_STATES.FAILURE,
+      });
+    });
+
+    it('should reject when status is REVERTED', async () => {
+      const mockCallback = createMockCallback([
+        { status: DEFAULT_POLLING_STATES.STARTED },
+        { status: DEFAULT_POLLING_STATES.REVERTED },
+      ]);
+
+      const pollPromise = poll(mockCallback);
+
+      jest.advanceTimersByTime(DEFAULT_POLLING_DELAY * 2);
+
+      await expect(pollPromise).rejects.toEqual({
+        status: DEFAULT_POLLING_STATES.REVERTED,
+      });
+    });
+
+    it('should reject when callback throws an error', async () => {
+      const mockCallback = jest
+        .fn()
+        .mockRejectedValue(new Error('Network error'));
+
+      const pollPromise = poll(mockCallback);
+
+      jest.advanceTimersByTime(DEFAULT_POLLING_DELAY);
+
+      await expect(pollPromise).rejects.toThrow('Network error');
+      expect(mockCallback).toHaveBeenCalledTimes(1);
+    });
+  });
+});
diff --git a/lib/constants.ts b/lib/constants.ts
index 94c30f6ad..993c6364b 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -11,14 +11,7 @@ export const CONFIG_FLAGS = {
   USE_CUSTOM_OBJECT_HUBFILE: 'useCustomObjectHubfile',
 } as const;
 
-export const POLLING_DELAY = 2000;
-
-export const POLLING_STATUS = {
-  SUCCESS: 'SUCCESS',
-  ERROR: 'ERROR',
-  REVERTED: 'REVERTED',
-  FAILURE: 'FAILURE',
-} as const;
+export const DEFAULT_POLLING_DELAY = 2000;
 
 export const PROJECT_CONFIG_FILE = 'hsproject.json' as const;
 
diff --git a/lib/polling.ts b/lib/polling.ts
index baced84f2..3c3a91d78 100644
--- a/lib/polling.ts
+++ b/lib/polling.ts
@@ -1,35 +1,47 @@
 import { HubSpotPromise } from '@hubspot/local-dev-lib/types/Http';
-import { ValueOf } from '@hubspot/local-dev-lib/types/Utils';
-import { POLLING_DELAY, POLLING_STATUS } from './constants';
+import { DEFAULT_POLLING_DELAY } from './constants';
+
+export const DEFAULT_POLLING_STATES = {
+  STARTED: 'STARTED',
+  SUCCESS: 'SUCCESS',
+  ERROR: 'ERROR',
+  REVERTED: 'REVERTED',
+  FAILURE: 'FAILURE',
+} as const;
+
+const DEFAULT_POLLING_STATUS_LOOKUP = {
+  successStates: [DEFAULT_POLLING_STATES.SUCCESS],
+  errorStates: [
+    DEFAULT_POLLING_STATES.ERROR,
+    DEFAULT_POLLING_STATES.REVERTED,
+    DEFAULT_POLLING_STATES.FAILURE,
+  ],
+};
 
 type GenericPollingResponse = {
-  status: ValueOf<typeof POLLING_STATUS>;
+  status: string;
 };
 
-type PollingCallback<T extends GenericPollingResponse> = (
-  accountId: number,
-  taskId: number | string
-) => HubSpotPromise<T>;
+type PollingCallback<T extends GenericPollingResponse> =
+  () => HubSpotPromise<T>;
 
 export function poll<T extends GenericPollingResponse>(
   callback: PollingCallback<T>,
-  accountId: number,
-  taskId: number | string
+  statusLookup: {
+    successStates: string[];
+    errorStates: string[];
+  } = DEFAULT_POLLING_STATUS_LOOKUP
 ): Promise<T> {
   return new Promise((resolve, reject) => {
     const pollInterval = setInterval(async () => {
       try {
-        const { data: pollResp } = await callback(accountId, taskId);
+        const { data: pollResp } = await callback();
         const { status } = pollResp;
 
-        if (status === POLLING_STATUS.SUCCESS) {
+        if (statusLookup.successStates.includes(status)) {
           clearInterval(pollInterval);
           resolve(pollResp);
-        } else if (
-          status === POLLING_STATUS.ERROR ||
-          status === POLLING_STATUS.REVERTED ||
-          status === POLLING_STATUS.FAILURE
-        ) {
+        } else if (statusLookup.errorStates.includes(status)) {
           clearInterval(pollInterval);
           reject(pollResp);
         }
@@ -37,6 +49,6 @@ export function poll<T extends GenericPollingResponse>(
         clearInterval(pollInterval);
         reject(error);
       }
-    }, POLLING_DELAY);
+    }, DEFAULT_POLLING_DELAY);
   });
 }
diff --git a/lib/projects/buildAndDeploy.ts b/lib/projects/buildAndDeploy.ts
index 83f2c5d63..b9841daff 100644
--- a/lib/projects/buildAndDeploy.ts
+++ b/lib/projects/buildAndDeploy.ts
@@ -16,7 +16,7 @@ import {
 import { WarnLogsResponse } from '@hubspot/local-dev-lib/types/Project';
 
 import {
-  POLLING_DELAY,
+  DEFAULT_POLLING_DELAY,
   PROJECT_BUILD_TEXT,
   PROJECT_DEPLOY_TEXT,
   PROJECT_TASK_TYPES,
@@ -366,7 +366,7 @@ function makePollTaskStatusFunc<T extends ProjectTask>({
             resolve(taskStatus);
           }
         }
-      }, POLLING_DELAY);
+      }, DEFAULT_POLLING_DELAY);
     });
   };
 }
@@ -377,7 +377,7 @@ function pollBuildAutodeployStatus(
   buildId: number
 ): Promise<Build> {
   return new Promise((resolve, reject) => {
-    let maxIntervals = (30 * 1000) / POLLING_DELAY; // Num of intervals in ~30s
+    let maxIntervals = (30 * 1000) / DEFAULT_POLLING_DELAY; // Num of intervals in ~30s
 
     const pollInterval = setInterval(async () => {
       let build: Build;
@@ -407,7 +407,7 @@ function pollBuildAutodeployStatus(
       } else {
         maxIntervals -= 1;
       }
-    }, POLLING_DELAY);
+    }, DEFAULT_POLLING_DELAY);
   });
 }
 
diff --git a/lib/projects/index.ts b/lib/projects/index.ts
index 963ca14ae..b73bf1954 100644
--- a/lib/projects/index.ts
+++ b/lib/projects/index.ts
@@ -18,7 +18,7 @@ import { HubSpotPromise } from '@hubspot/local-dev-lib/types/Http';
 
 import {
   FEEDBACK_INTERVAL,
-  POLLING_DELAY,
+  DEFAULT_POLLING_DELAY,
   PROJECT_CONFIG_FILE,
   HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH,
   PROJECT_COMPONENT_TYPES,
@@ -242,7 +242,7 @@ async function pollFetchProject(
           reject(err);
         }
       }
-    }, POLLING_DELAY);
+    }, DEFAULT_POLLING_DELAY);
   });
 }
 
diff --git a/package.json b/package.json
index ffe9f9ce4..c43f20cd4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@hubspot/cli",
-  "version": "7.0.1",
+  "version": "7.0.2",
   "description": "The official CLI for developing on HubSpot",
   "license": "Apache-2.0",
   "repository": "https://github.com/HubSpot/hubspot-cli",
@@ -8,7 +8,7 @@
     "@hubspot/local-dev-lib": "3.1.3",
     "@hubspot/serverless-dev-runtime": "7.0.2",
     "@hubspot/theme-preview-dev-server": "0.0.10",
-    "@hubspot/ui-extensions-dev-server": "0.8.40",
+    "@hubspot/ui-extensions-dev-server": "0.8.42",
     "archiver": "7.0.1",
     "chalk": "4.1.2",
     "chokidar": "3.6.0",