mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Allow EDU accounts to fetch cloud config bundles (#25963)
## Summary Allow EDU ChatGPT workspaces to fetch cloud config bundles. The existing cloud config eligibility gate only allowed business-like and enterprise plans, which meant EDU admins could configure managed policies in the UI but the Codex client would skip fetching them. This keeps individual/pro and team-like usage-based plans excluded, and adds service-level coverage for both `edu` and `education` plan aliases. ## Validation - `just fmt` - `just test -p codex-cloud-config` - Built the Codex app locally, created a new EDU ChatGPT workspace, and verified config bundles can be fetched and are properly applied.
This commit is contained in:
1 parent
271d5cecf2
commit
1fd2a6d328
2 files changed
+43
-57
No files matched your search
@@ -49,7 +49,8 @@ fn cloud_config_eligible_auth(auth: &CodexAuth) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
auth.uses_codex_backend()
|
auth.uses_codex_backend()
|
||||||
&& (plan_type.is_business_like() || matches!(plan_type, PlanType::Enterprise))
|
&& (plan_type.is_business_like()
|
||||||
|
|| matches!(plan_type, PlanType::Enterprise | PlanType::Edu))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optional_bundle(bundle: CloudConfigBundle) -> Option<CloudConfigBundle> {
|
fn optional_bundle(bundle: CloudConfigBundle) -> Option<CloudConfigBundle> {
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ async fn get_bundle_skips_non_chatgpt_auth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_bundle_skips_non_business_or_enterprise_plan() {
|
async fn get_bundle_skips_individual_plan() {
|
||||||
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
||||||
let codex_home = tempdir().expect("tempdir");
|
let codex_home = tempdir().expect("tempdir");
|
||||||
let service = CloudConfigBundleService::new(
|
let service = CloudConfigBundleService::new(
|
||||||
@@ -365,6 +365,46 @@ async fn get_bundle_skips_non_business_or_enterprise_plan() {
|
|||||||
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 0);
|
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_bundle_allows_eligible_workspace_plans_and_writes_cache() {
|
||||||
|
for plan_type in [
|
||||||
|
"business",
|
||||||
|
"enterprise_cbp_usage_based",
|
||||||
|
"enterprise",
|
||||||
|
"hc",
|
||||||
|
"edu",
|
||||||
|
"education",
|
||||||
|
] {
|
||||||
|
let bundle = test_bundle();
|
||||||
|
let fetcher = Arc::new(StaticBundleClient::new(bundle.clone()));
|
||||||
|
let codex_home = tempdir().expect("tempdir");
|
||||||
|
let service = CloudConfigBundleService::new(
|
||||||
|
auth_manager_with_plan(plan_type).await,
|
||||||
|
fetcher.clone(),
|
||||||
|
codex_home.path().to_path_buf(),
|
||||||
|
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
service.load_startup_bundle().await,
|
||||||
|
Ok(Some(bundle)),
|
||||||
|
"plan_type: {plan_type}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fetcher.request_count.load(Ordering::SeqCst),
|
||||||
|
1,
|
||||||
|
"plan_type: {plan_type}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
codex_home
|
||||||
|
.path()
|
||||||
|
.join(CLOUD_CONFIG_BUNDLE_CACHE_FILENAME)
|
||||||
|
.exists(),
|
||||||
|
"plan_type: {plan_type}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_bundle_skips_team_like_usage_based_plan() {
|
async fn get_bundle_skips_team_like_usage_based_plan() {
|
||||||
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
||||||
@@ -380,31 +420,6 @@ async fn get_bundle_skips_team_like_usage_based_plan() {
|
|||||||
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 0);
|
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_bundle_allows_business_plan_and_writes_cache() {
|
|
||||||
let bundle = test_bundle();
|
|
||||||
let codex_home = tempdir().expect("tempdir");
|
|
||||||
let fetcher = Arc::new(StaticBundleClient::new(bundle.clone()));
|
|
||||||
let service = CloudConfigBundleService::new(
|
|
||||||
auth_manager_with_plan("business").await,
|
|
||||||
fetcher.clone(),
|
|
||||||
codex_home.path().to_path_buf(),
|
|
||||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
service.load_startup_bundle().await,
|
|
||||||
Ok(Some(bundle.clone()))
|
|
||||||
);
|
|
||||||
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 1);
|
|
||||||
assert!(
|
|
||||||
codex_home
|
|
||||||
.path()
|
|
||||||
.join(CLOUD_CONFIG_BUNDLE_CACHE_FILENAME)
|
|
||||||
.exists()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_bundle_rejects_invalid_remote_bundle_before_cache_write() {
|
async fn get_bundle_rejects_invalid_remote_bundle_before_cache_write() {
|
||||||
let codex_home = tempdir().expect("tempdir");
|
let codex_home = tempdir().expect("tempdir");
|
||||||
@@ -468,36 +483,6 @@ async fn get_bundle_ignores_invalid_cache_and_refetches() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_bundle_allows_business_like_usage_based_plan() {
|
|
||||||
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
|
||||||
let codex_home = tempdir().expect("tempdir");
|
|
||||||
let service = CloudConfigBundleService::new(
|
|
||||||
auth_manager_with_plan("enterprise_cbp_usage_based").await,
|
|
||||||
fetcher.clone(),
|
|
||||||
codex_home.path().to_path_buf(),
|
|
||||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(service.load_startup_bundle().await, Ok(Some(test_bundle())));
|
|
||||||
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_bundle_allows_hc_plan_as_enterprise() {
|
|
||||||
let fetcher = Arc::new(StaticBundleClient::new(test_bundle()));
|
|
||||||
let codex_home = tempdir().expect("tempdir");
|
|
||||||
let service = CloudConfigBundleService::new(
|
|
||||||
auth_manager_with_plan("hc").await,
|
|
||||||
fetcher.clone(),
|
|
||||||
codex_home.path().to_path_buf(),
|
|
||||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(service.load_startup_bundle().await, Ok(Some(test_bundle())));
|
|
||||||
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_bundle_empty_response_is_success_and_cached() {
|
async fn get_bundle_empty_response_is_success_and_cached() {
|
||||||
let codex_home = tempdir().expect("tempdir");
|
let codex_home = tempdir().expect("tempdir");
|
||||||
|
|||||||
Reference in new issue
Block a user