Add buffer integration test
Rearrange channel crate structure Get channel buffer from database co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
ff5035ea37
commit
a7a4e2e369
33 changed files with 403 additions and 39 deletions
41
Cargo.lock
generated
41
Cargo.lock
generated
|
@ -1063,6 +1063,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"audio",
|
"audio",
|
||||||
|
"channel",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"fs",
|
"fs",
|
||||||
|
@ -1190,6 +1191,41 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "channel"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"client",
|
||||||
|
"collections",
|
||||||
|
"db",
|
||||||
|
"futures 0.3.28",
|
||||||
|
"gpui",
|
||||||
|
"image",
|
||||||
|
"language",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
|
"postage",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rpc",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"settings",
|
||||||
|
"smol",
|
||||||
|
"staff_mode",
|
||||||
|
"sum_tree",
|
||||||
|
"tempfile",
|
||||||
|
"text",
|
||||||
|
"thiserror",
|
||||||
|
"time 0.3.24",
|
||||||
|
"tiny_http",
|
||||||
|
"url",
|
||||||
|
"util",
|
||||||
|
"uuid 1.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.26"
|
version = "0.4.26"
|
||||||
|
@ -1354,6 +1390,7 @@ dependencies = [
|
||||||
"staff_mode",
|
"staff_mode",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"text",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.24",
|
"time 0.3.24",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
|
@ -1418,6 +1455,7 @@ dependencies = [
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"call",
|
"call",
|
||||||
|
"channel",
|
||||||
"clap 3.2.25",
|
"clap 3.2.25",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
|
@ -1480,6 +1518,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"auto_update",
|
"auto_update",
|
||||||
"call",
|
"call",
|
||||||
|
"channel",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
|
@ -9536,6 +9575,7 @@ dependencies = [
|
||||||
"async-recursion 1.0.4",
|
"async-recursion 1.0.4",
|
||||||
"bincode",
|
"bincode",
|
||||||
"call",
|
"call",
|
||||||
|
"channel",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"context_menu",
|
"context_menu",
|
||||||
|
@ -9661,6 +9701,7 @@ dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"breadcrumbs",
|
"breadcrumbs",
|
||||||
"call",
|
"call",
|
||||||
|
"channel",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cli",
|
"cli",
|
||||||
"client",
|
"client",
|
||||||
|
|
|
@ -6,6 +6,7 @@ members = [
|
||||||
"crates/auto_update",
|
"crates/auto_update",
|
||||||
"crates/breadcrumbs",
|
"crates/breadcrumbs",
|
||||||
"crates/call",
|
"crates/call",
|
||||||
|
"crates/channel",
|
||||||
"crates/cli",
|
"crates/cli",
|
||||||
"crates/client",
|
"crates/client",
|
||||||
"crates/clock",
|
"crates/clock",
|
||||||
|
|
|
@ -20,6 +20,7 @@ test-support = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
audio = { path = "../audio" }
|
audio = { path = "../audio" }
|
||||||
|
channel = { path = "../channel" }
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
|
|
@ -7,9 +7,8 @@ use std::sync::Arc;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use audio::Audio;
|
use audio::Audio;
|
||||||
use call_settings::CallSettings;
|
use call_settings::CallSettings;
|
||||||
use client::{
|
use channel::ChannelId;
|
||||||
proto, ChannelId, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore,
|
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
|
||||||
};
|
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::{future::Shared, FutureExt};
|
use futures::{future::Shared, FutureExt};
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
|
|
51
crates/channel/Cargo.toml
Normal file
51
crates/channel/Cargo.toml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
[package]
|
||||||
|
name = "channel"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/channel.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
client = { path = "../client" }
|
||||||
|
collections = { path = "../collections" }
|
||||||
|
db = { path = "../db" }
|
||||||
|
gpui = { path = "../gpui" }
|
||||||
|
util = { path = "../util" }
|
||||||
|
rpc = { path = "../rpc" }
|
||||||
|
text = { path = "../text" }
|
||||||
|
language = { path = "../language" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
|
staff_mode = { path = "../staff_mode" }
|
||||||
|
sum_tree = { path = "../sum_tree" }
|
||||||
|
|
||||||
|
anyhow.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
image = "0.23"
|
||||||
|
lazy_static.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
parking_lot.workspace = true
|
||||||
|
postage.workspace = true
|
||||||
|
rand.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
|
smol.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
time.workspace = true
|
||||||
|
tiny_http = "0.8"
|
||||||
|
uuid = { version = "1.1.2", features = ["v4"] }
|
||||||
|
url = "2.2"
|
||||||
|
serde.workspace = true
|
||||||
|
serde_derive.workspace = true
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
collections = { path = "../collections", features = ["test-support"] }
|
||||||
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
|
rpc = { path = "../rpc", features = ["test-support"] }
|
||||||
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
|
util = { path = "../util", features = ["test-support"] }
|
7
crates/channel/src/channel.rs
Normal file
7
crates/channel/src/channel.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod channel_store;
|
||||||
|
|
||||||
|
pub mod channel_buffer;
|
||||||
|
pub use channel_store::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod channel_store_tests;
|
80
crates/channel/src/channel_buffer.rs
Normal file
80
crates/channel/src/channel_buffer.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::ChannelId;
|
||||||
|
use anyhow::Result;
|
||||||
|
use client::Client;
|
||||||
|
use gpui::{Entity, ModelContext, ModelHandle, Task};
|
||||||
|
use rpc::proto::GetChannelBuffer;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
// Open the channel document
|
||||||
|
// ChannelDocumentView { ChannelDocument, Editor } -> On clone, clones internal ChannelDocument handle, instantiates new editor
|
||||||
|
// Produces a view which is: (ChannelDocument, Editor), ChannelDocument manages subscriptions
|
||||||
|
// ChannelDocuments -> Buffers -> Editor with that buffer
|
||||||
|
|
||||||
|
// ChannelDocuments {
|
||||||
|
// ChannleBuffers: HashMap<bufferId, ModelHandle<language::Buffer>>
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub struct ChannelBuffer {
|
||||||
|
channel_id: ChannelId,
|
||||||
|
buffer: Option<ModelHandle<language::Buffer>>,
|
||||||
|
client: Arc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for ChannelBuffer {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelBuffer {
|
||||||
|
pub fn for_channel(
|
||||||
|
channel_id: ChannelId,
|
||||||
|
client: Arc<Client>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
channel_id,
|
||||||
|
client,
|
||||||
|
buffer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_buffer_update(
|
||||||
|
&mut self,
|
||||||
|
buffer: ModelHandle<language::Buffer>,
|
||||||
|
event: &language::Event,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Task<Result<ModelHandle<language::Buffer>>> {
|
||||||
|
if let Some(buffer) = &self.buffer {
|
||||||
|
Task::ready(Ok(buffer.clone()))
|
||||||
|
} else {
|
||||||
|
let channel_id = self.channel_id;
|
||||||
|
let client = self.client.clone();
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
let response = client.request(GetChannelBuffer { channel_id }).await?;
|
||||||
|
|
||||||
|
let base_text = response.base_text;
|
||||||
|
let operations = response
|
||||||
|
.operations
|
||||||
|
.into_iter()
|
||||||
|
.map(language::proto::deserialize_operation)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
let buffer = cx.add_model(|cx| language::Buffer::new(0, base_text, cx));
|
||||||
|
buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
|
||||||
|
|
||||||
|
cx.subscribe(&buffer, Self::on_buffer_update).detach();
|
||||||
|
|
||||||
|
this.buffer = Some(buffer.clone());
|
||||||
|
anyhow::Ok(buffer)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::Status;
|
|
||||||
use crate::{Client, Subscription, User, UserStore};
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use client::Status;
|
||||||
|
use client::UserId;
|
||||||
|
use client::{Client, Subscription, User, UserStore};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
|
@ -13,7 +14,6 @@ use std::sync::Arc;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub type ChannelId = u64;
|
pub type ChannelId = u64;
|
||||||
pub type UserId = u64;
|
|
||||||
|
|
||||||
pub struct ChannelStore {
|
pub struct ChannelStore {
|
||||||
channels_by_id: HashMap<ChannelId, Arc<Channel>>,
|
channels_by_id: HashMap<ChannelId, Arc<Channel>>,
|
|
@ -1,4 +1,7 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use client::{Client, UserStore};
|
||||||
|
use gpui::{AppContext, ModelHandle};
|
||||||
|
use rpc::proto;
|
||||||
use util::http::FakeHttpClient;
|
use util::http::FakeHttpClient;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
|
@ -17,6 +17,7 @@ db = { path = "../db" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
|
text = { path = "../text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
staff_mode = { path = "../staff_mode" }
|
staff_mode = { path = "../staff_mode" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod channel_store_tests;
|
|
||||||
|
|
||||||
pub mod channel_store;
|
|
||||||
pub mod telemetry;
|
pub mod telemetry;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
|
@ -48,7 +44,6 @@ use util::channel::ReleaseChannel;
|
||||||
use util::http::HttpClient;
|
use util::http::HttpClient;
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
|
||||||
pub use channel_store::*;
|
|
||||||
pub use rpc::*;
|
pub use rpc::*;
|
||||||
pub use telemetry::ClickhouseEvent;
|
pub use telemetry::ClickhouseEvent;
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
|
|
@ -10,9 +10,11 @@ use std::sync::{Arc, Weak};
|
||||||
use util::http::HttpClient;
|
use util::http::HttpClient;
|
||||||
use util::TryFutureExt as _;
|
use util::TryFutureExt as _;
|
||||||
|
|
||||||
|
pub type UserId = u64;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: u64,
|
pub id: UserId,
|
||||||
pub github_login: String,
|
pub github_login: String,
|
||||||
pub avatar: Option<Arc<ImageData>>,
|
pub avatar: Option<Arc<ImageData>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ collections = { path = "../collections", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
call = { path = "../call", features = ["test-support"] }
|
call = { path = "../call", features = ["test-support"] }
|
||||||
client = { path = "../client", features = ["test-support"] }
|
client = { path = "../client", features = ["test-support"] }
|
||||||
|
channel = { path = "../channel" }
|
||||||
editor = { path = "../editor", features = ["test-support"] }
|
editor = { path = "../editor", features = ["test-support"] }
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
fs = { path = "../fs", features = ["test-support"] }
|
fs = { path = "../fs", features = ["test-support"] }
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod db_tests;
|
pub mod tests;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_db;
|
pub use tests::TestDb;
|
||||||
|
|
||||||
mod ids;
|
mod ids;
|
||||||
mod queries;
|
mod queries;
|
||||||
|
|
|
@ -9,6 +9,3 @@ pub mod rooms;
|
||||||
pub mod servers;
|
pub mod servers;
|
||||||
pub mod signups;
|
pub mod signups;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod buffer_tests;
|
|
||||||
|
|
|
@ -689,6 +689,34 @@ impl Database {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_or_create_buffer_for_channel(
|
||||||
|
&self,
|
||||||
|
channel_id: ChannelId,
|
||||||
|
) -> Result<BufferId> {
|
||||||
|
self.transaction(|tx| async move {
|
||||||
|
let tx = tx;
|
||||||
|
let channel = channel::Entity::find_by_id(channel_id)
|
||||||
|
.one(&*tx)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| anyhow!("invalid channel"))?;
|
||||||
|
|
||||||
|
if let Some(id) = channel.main_buffer_id {
|
||||||
|
return Ok(id);
|
||||||
|
} else {
|
||||||
|
let buffer = buffer::ActiveModel::new().insert(&*tx).await?;
|
||||||
|
channel::ActiveModel {
|
||||||
|
id: ActiveValue::Unchanged(channel_id),
|
||||||
|
main_buffer_id: ActiveValue::Set(Some(buffer.id)),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.update(&*tx)
|
||||||
|
.await?;
|
||||||
|
Ok(buffer.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::db::ChannelId;
|
use crate::db::{BufferId, ChannelId};
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
@ -7,6 +7,7 @@ pub struct Model {
|
||||||
#[sea_orm(primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
pub id: ChannelId,
|
pub id: ChannelId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub main_buffer_id: Option<BufferId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
mod buffer_tests;
|
||||||
|
mod db_tests;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::executor::Background;
|
use gpui::executor::Background;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -96,7 +99,7 @@ macro_rules! test_both_dbs {
|
||||||
($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => {
|
($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn $postgres_test_name() {
|
async fn $postgres_test_name() {
|
||||||
let test_db = crate::db::test_db::TestDb::postgres(
|
let test_db = crate::db::TestDb::postgres(
|
||||||
gpui::executor::Deterministic::new(0).build_background(),
|
gpui::executor::Deterministic::new(0).build_background(),
|
||||||
);
|
);
|
||||||
$test_name(test_db.db()).await;
|
$test_name(test_db.db()).await;
|
||||||
|
@ -104,9 +107,8 @@ macro_rules! test_both_dbs {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn $sqlite_test_name() {
|
async fn $sqlite_test_name() {
|
||||||
let test_db = crate::db::test_db::TestDb::sqlite(
|
let test_db =
|
||||||
gpui::executor::Deterministic::new(0).build_background(),
|
crate::db::TestDb::sqlite(gpui::executor::Deterministic::new(0).build_background());
|
||||||
);
|
|
||||||
$test_name(test_db.db()).await;
|
$test_name(test_db.db()).await;
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -3,7 +3,7 @@ use crate::test_both_dbs;
|
||||||
use gpui::executor::{Background, Deterministic};
|
use gpui::executor::{Background, Deterministic};
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
use pretty_assertions::{assert_eq, assert_ne};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use test_db::TestDb;
|
use tests::TestDb;
|
||||||
|
|
||||||
test_both_dbs!(
|
test_both_dbs!(
|
||||||
test_get_users,
|
test_get_users,
|
||||||
|
@ -1329,6 +1329,35 @@ async fn test_channel_renames(db: &Arc<Database>) {
|
||||||
assert!(bad_name_rename.is_err())
|
assert!(bad_name_rename.is_err())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_both_dbs!(
|
||||||
|
test_get_or_create_channel_buffer,
|
||||||
|
test_get_or_create_channel_buffer_postgres,
|
||||||
|
test_get_or_create_channel_buffer_sqlite
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn test_get_or_create_channel_buffer(db: &Arc<Database>) {
|
||||||
|
let a_id = db
|
||||||
|
.create_user(
|
||||||
|
"user1@example.com",
|
||||||
|
false,
|
||||||
|
NewUserParams {
|
||||||
|
github_login: "user1".into(),
|
||||||
|
github_user_id: 5,
|
||||||
|
invite_count: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.user_id;
|
||||||
|
|
||||||
|
let zed_id = db.create_root_channel("zed", "1", a_id).await.unwrap();
|
||||||
|
|
||||||
|
let first_buffer_id = db.get_or_create_buffer_for_channel(zed_id).await.unwrap();
|
||||||
|
let second_buffer_id = db.get_or_create_buffer_for_channel(zed_id).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(first_buffer_id, second_buffer_id);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_multiple_signup_overwrite() {
|
async fn test_multiple_signup_overwrite() {
|
||||||
let test_db = TestDb::postgres(build_background_executor());
|
let test_db = TestDb::postgres(build_background_executor());
|
|
@ -35,8 +35,8 @@ use lazy_static::lazy_static;
|
||||||
use prometheus::{register_int_gauge, IntGauge};
|
use prometheus::{register_int_gauge, IntGauge};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
proto::{
|
proto::{
|
||||||
self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, LiveKitConnectionInfo,
|
self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, GetChannelBufferResponse,
|
||||||
RequestMessage,
|
LiveKitConnectionInfo, RequestMessage,
|
||||||
},
|
},
|
||||||
Connection, ConnectionId, Peer, Receipt, TypedEnvelope,
|
Connection, ConnectionId, Peer, Receipt, TypedEnvelope,
|
||||||
};
|
};
|
||||||
|
@ -248,6 +248,7 @@ impl Server {
|
||||||
.add_request_handler(remove_channel_member)
|
.add_request_handler(remove_channel_member)
|
||||||
.add_request_handler(set_channel_member_admin)
|
.add_request_handler(set_channel_member_admin)
|
||||||
.add_request_handler(rename_channel)
|
.add_request_handler(rename_channel)
|
||||||
|
.add_request_handler(get_channel_buffer)
|
||||||
.add_request_handler(get_channel_members)
|
.add_request_handler(get_channel_members)
|
||||||
.add_request_handler(respond_to_channel_invite)
|
.add_request_handler(respond_to_channel_invite)
|
||||||
.add_request_handler(join_channel)
|
.add_request_handler(join_channel)
|
||||||
|
@ -2478,6 +2479,26 @@ async fn join_channel(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_channel_buffer(
|
||||||
|
request: proto::GetChannelBuffer,
|
||||||
|
response: Response<proto::GetChannelBuffer>,
|
||||||
|
session: Session,
|
||||||
|
) -> Result<()> {
|
||||||
|
let db = session.db().await;
|
||||||
|
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||||
|
|
||||||
|
let buffer_id = db.get_or_create_buffer_for_channel(channel_id).await?;
|
||||||
|
|
||||||
|
let buffer = db.get_buffer(buffer_id).await?;
|
||||||
|
|
||||||
|
response.send(GetChannelBufferResponse {
|
||||||
|
base_text: buffer.base_text,
|
||||||
|
operations: buffer.operations,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_diff_base(request: proto::UpdateDiffBase, session: Session) -> Result<()> {
|
async fn update_diff_base(request: proto::UpdateDiffBase, session: Session) -> Result<()> {
|
||||||
let project_id = ProjectId::from_proto(request.project_id);
|
let project_id = ProjectId::from_proto(request.project_id);
|
||||||
let project_connection_ids = session
|
let project_connection_ids = session
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{test_db::TestDb, NewUserParams, UserId},
|
db::{tests::TestDb, NewUserParams, UserId},
|
||||||
executor::Executor,
|
executor::Executor,
|
||||||
rpc::{Server, CLEANUP_TIMEOUT},
|
rpc::{Server, CLEANUP_TIMEOUT},
|
||||||
AppState,
|
AppState,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use call::{ActiveCall, Room};
|
use call::{ActiveCall, Room};
|
||||||
|
use channel::ChannelStore;
|
||||||
use client::{
|
use client::{
|
||||||
self, proto::PeerId, ChannelStore, Client, Connection, Credentials, EstablishConnectionError,
|
self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
|
||||||
UserStore,
|
|
||||||
};
|
};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
|
@ -31,6 +31,7 @@ use std::{
|
||||||
use util::http::FakeHttpClient;
|
use util::http::FakeHttpClient;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
mod channel_buffer_tests;
|
||||||
mod channel_tests;
|
mod channel_tests;
|
||||||
mod integration_tests;
|
mod integration_tests;
|
||||||
mod randomized_integration_tests;
|
mod randomized_integration_tests;
|
||||||
|
|
84
crates/collab/src/tests/channel_buffer_tests.rs
Normal file
84
crates/collab/src/tests/channel_buffer_tests.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use crate::tests::TestServer;
|
||||||
|
|
||||||
|
use channel::channel_buffer::ChannelBuffer;
|
||||||
|
use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
|
||||||
|
use std::{ops::Range, sync::Arc};
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_channel_buffers(
|
||||||
|
deterministic: Arc<Deterministic>,
|
||||||
|
cx_a: &mut TestAppContext,
|
||||||
|
cx_b: &mut TestAppContext,
|
||||||
|
) {
|
||||||
|
deterministic.forbid_parking();
|
||||||
|
let mut server = TestServer::start(&deterministic).await;
|
||||||
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
|
||||||
|
let zed_id = server
|
||||||
|
.make_channel("zed", (&client_a, cx_a), &mut [(&client_b, cx_b)])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let a_document =
|
||||||
|
cx_a.add_model(|cx| ChannelBuffer::for_channel(zed_id, client_a.client().to_owned(), cx));
|
||||||
|
let channel_buffer_a = a_document
|
||||||
|
.update(cx_a, |doc, cx| doc.buffer(cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
edit_channel_buffer(&channel_buffer_a, cx_a, [(0..0, "hello world")]);
|
||||||
|
edit_channel_buffer(&channel_buffer_a, cx_a, [(5..5, ", cruel")]);
|
||||||
|
edit_channel_buffer(&channel_buffer_a, cx_a, [(0..5, "goodbye")]);
|
||||||
|
undo_channel_buffer(&channel_buffer_a, cx_a);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
channel_buffer_text(&channel_buffer_a, cx_a),
|
||||||
|
"hello, cruel world"
|
||||||
|
);
|
||||||
|
|
||||||
|
let b_document =
|
||||||
|
cx_b.add_model(|cx| ChannelBuffer::for_channel(zed_id, client_b.client().to_owned(), cx));
|
||||||
|
let channel_buffer_b = b_document
|
||||||
|
.update(cx_b, |doc, cx| doc.buffer(cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
channel_buffer_text(&channel_buffer_b, cx_b),
|
||||||
|
"hello, cruel world"
|
||||||
|
);
|
||||||
|
|
||||||
|
edit_channel_buffer(&channel_buffer_b, cx_b, [(7..12, "beautiful")]);
|
||||||
|
|
||||||
|
deterministic.run_until_parked();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
channel_buffer_text(&channel_buffer_a, cx_a),
|
||||||
|
"hello, beautiful world"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
channel_buffer_text(&channel_buffer_b, cx_b),
|
||||||
|
"hello, beautiful world"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_channel_buffer<I>(
|
||||||
|
channel_buffer: &ModelHandle<language::Buffer>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
edits: I,
|
||||||
|
) where
|
||||||
|
I: IntoIterator<Item = (Range<usize>, &'static str)>,
|
||||||
|
{
|
||||||
|
channel_buffer.update(cx, |buffer, cx| buffer.edit(edits, None, cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn undo_channel_buffer(channel_buffer: &ModelHandle<language::Buffer>, cx: &mut TestAppContext) {
|
||||||
|
channel_buffer.update(cx, |buffer, cx| buffer.undo(cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn channel_buffer_text(
|
||||||
|
channel_buffer: &ModelHandle<language::Buffer>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> String {
|
||||||
|
channel_buffer.read_with(cx, |buffer, _| buffer.text())
|
||||||
|
}
|
|
@ -3,7 +3,8 @@ use crate::{
|
||||||
tests::{room_participants, RoomParticipants, TestServer},
|
tests::{room_participants, RoomParticipants, TestServer},
|
||||||
};
|
};
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{ChannelId, ChannelMembership, ChannelStore, User};
|
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||||
|
use client::User;
|
||||||
use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
|
use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
|
||||||
use rpc::{proto, RECEIVE_TIMEOUT};
|
use rpc::{proto, RECEIVE_TIMEOUT};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
@ -26,6 +26,7 @@ auto_update = { path = "../auto_update" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
call = { path = "../call" }
|
call = { path = "../call" }
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
|
channel = { path = "../channel" }
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
context_menu = { path = "../context_menu" }
|
context_menu = { path = "../context_menu" }
|
||||||
|
|
|
@ -4,10 +4,8 @@ mod panel_settings;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{
|
use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
|
||||||
proto::PeerId, Channel, ChannelEvent, ChannelId, ChannelStore, Client, Contact, User, UserStore,
|
use client::{proto::PeerId, Client, Contact, User, UserStore};
|
||||||
};
|
|
||||||
|
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{Cancel, Editor};
|
use editor::{Cancel, Editor};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use client::{proto, ChannelId, ChannelMembership, ChannelStore, User, UserId, UserStore};
|
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||||
|
use client::{proto, User, UserId, UserStore};
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -142,6 +142,9 @@ message Envelope {
|
||||||
GetChannelMembersResponse get_channel_members_response = 128;
|
GetChannelMembersResponse get_channel_members_response = 128;
|
||||||
SetChannelMemberAdmin set_channel_member_admin = 129;
|
SetChannelMemberAdmin set_channel_member_admin = 129;
|
||||||
RenameChannel rename_channel = 130;
|
RenameChannel rename_channel = 130;
|
||||||
|
|
||||||
|
GetChannelBuffer get_channel_buffer = 131;
|
||||||
|
GetChannelBufferResponse get_channel_buffer_response = 132;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,6 +951,15 @@ message RenameChannel {
|
||||||
string name = 2;
|
string name = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetChannelBuffer {
|
||||||
|
uint64 channel_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetChannelBufferResponse {
|
||||||
|
string base_text = 1;
|
||||||
|
repeated Operation operations = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message RespondToChannelInvite {
|
message RespondToChannelInvite {
|
||||||
uint64 channel_id = 1;
|
uint64 channel_id = 1;
|
||||||
bool accept = 2;
|
bool accept = 2;
|
||||||
|
|
|
@ -248,7 +248,9 @@ messages!(
|
||||||
(GetPrivateUserInfo, Foreground),
|
(GetPrivateUserInfo, Foreground),
|
||||||
(GetPrivateUserInfoResponse, Foreground),
|
(GetPrivateUserInfoResponse, Foreground),
|
||||||
(GetChannelMembers, Foreground),
|
(GetChannelMembers, Foreground),
|
||||||
(GetChannelMembersResponse, Foreground)
|
(GetChannelMembersResponse, Foreground),
|
||||||
|
(GetChannelBuffer, Foreground),
|
||||||
|
(GetChannelBufferResponse, Foreground)
|
||||||
);
|
);
|
||||||
|
|
||||||
request_messages!(
|
request_messages!(
|
||||||
|
@ -315,6 +317,7 @@ request_messages!(
|
||||||
(UpdateParticipantLocation, Ack),
|
(UpdateParticipantLocation, Ack),
|
||||||
(UpdateProject, Ack),
|
(UpdateProject, Ack),
|
||||||
(UpdateWorktree, Ack),
|
(UpdateWorktree, Ack),
|
||||||
|
(GetChannelBuffer, GetChannelBufferResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
|
|
|
@ -22,6 +22,7 @@ test-support = [
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
call = { path = "../call" }
|
call = { path = "../call" }
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
|
channel = { path = "../channel" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
context_menu = { path = "../context_menu" }
|
context_menu = { path = "../context_menu" }
|
||||||
drag_and_drop = { path = "../drag_and_drop" }
|
drag_and_drop = { path = "../drag_and_drop" }
|
||||||
|
|
|
@ -12,9 +12,10 @@ mod workspace_settings;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
|
use channel::ChannelStore;
|
||||||
use client::{
|
use client::{
|
||||||
proto::{self, PeerId},
|
proto::{self, PeerId},
|
||||||
ChannelStore, Client, TypedEnvelope, UserStore,
|
Client, TypedEnvelope, UserStore,
|
||||||
};
|
};
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use drag_and_drop::DragAndDrop;
|
use drag_and_drop::DragAndDrop;
|
||||||
|
|
|
@ -21,6 +21,7 @@ activity_indicator = { path = "../activity_indicator" }
|
||||||
auto_update = { path = "../auto_update" }
|
auto_update = { path = "../auto_update" }
|
||||||
breadcrumbs = { path = "../breadcrumbs" }
|
breadcrumbs = { path = "../breadcrumbs" }
|
||||||
call = { path = "../call" }
|
call = { path = "../call" }
|
||||||
|
channel = { path = "../channel" }
|
||||||
cli = { path = "../cli" }
|
cli = { path = "../cli" }
|
||||||
collab_ui = { path = "../collab_ui" }
|
collab_ui = { path = "../collab_ui" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
|
|
|
@ -3,13 +3,12 @@
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
|
use channel::ChannelStore;
|
||||||
use cli::{
|
use cli::{
|
||||||
ipc::{self, IpcSender},
|
ipc::{self, IpcSender},
|
||||||
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
||||||
};
|
};
|
||||||
use client::{
|
use client::{self, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
||||||
self, ChannelStore, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
|
||||||
};
|
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||||
use futures::{
|
use futures::{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue