"""Relative quantity and condition edits keep the previously confirmed quote basis.""" from pathlib import Path import tempfile import unittest from unittest.mock import patch from logistics_chat import create_app,db from logistics_chat.quote_data import import_snapshot from test_quote_timing import timing_snapshot from test_quotes import frames class QuoteChangeTests(unittest.TestCase): def setUp(self): folder=tempfile.TemporaryDirectory();self.addCleanup(folder.cleanup) root=Path(folder.name) self.app=create_app({'TESTING':True,'DATABASE':root/'test.db','UPLOAD_DIR':root/'uploads', 'BACKUP_DIR':root/'backups','LOG_DIR':root/'logs','DEEPSEEK_API_KEY':'','RATE_LIMIT_PER_MIN':1000}) self.client=self.app.test_client() self.client.post('/api/visitor/session',json={'key':'5'*64}) self.cid=self.client.post('/api/new_chat').json['id'] with self.app.app_context(): c=db.connect();import_snapshot(c,timing_snapshot(),activate=True);c.commit();c.close() def ask(self,question): with patch('logistics_chat.ai_client.post_completion') as model: events=frames(self.client.post('/stream_chat',json={'msg':question,'conv_id':self.cid})) model.assert_not_called() self.assertEqual(events[-1],{'d':1},events) self.assertFalse(any(event.get('e') for event in events),events) return events[0]['t'] def test_weight_increments_are_added_and_absolute_changes_replace_the_total(self): self.ask('美国1000KG义乌美东普船海派报价') self.assertIn('1100KG = 6600元',self.ask('再加100KG,其他不变')) self.assertIn('900KG = 5400元',self.ask('再减掉200KG')) self.assertIn('900KG = 5400元',self.ask('那一共多少钱')) self.assertIn('800KG = 4800元',self.ask('减到800KG')) self.assertIn('900KG = 5400元',self.ask('增加0.1吨,总重900KG')) def test_invalid_or_unitless_changes_stay_pending_until_a_new_total_is_confirmed(self): self.ask('美国30KG义乌美西普船海派报价') self.assertIn('调整后的总重需要大于0',self.ask('减去40KG')) text=self.ask('换成美东') self.assertIn('请提供调整后的总重',text) self.assertNotIn('KG =',text) self.assertIn('20KG = 120元',self.ask('总重20KG')) self.assertIn('请提供调整后的总重',self.ask('再加100')) self.assertNotIn('KG =',self.ask('具体多少')) self.assertIn('50KG = 300元',self.ask('新总重50KG')) def test_relative_quantity_without_a_confirmed_starting_value_requests_a_total(self): self.assertIn('原来的总重',self.ask('再加100KG')) self.assertIn('调整后的总重',self.ask('美国普船海派')) self.assertIn('200KG = 800元',self.ask('总重200KG,义乌美西')) def test_uniform_cartons_add_and_remove_without_losing_the_previous_cartons(self): self.ask('美国10箱,每箱20KG,义乌美西普船海派报价') self.assertIn('240KG = 960元',self.ask('再加2箱,每箱20KG')) text=self.ask('减少1箱') self.assertIn('220KG = 880元',text) self.assertIn('共11箱',text) self.assertIn('220KG = 880元',self.ask('那一共呢')) def test_added_carton_dimensions_apply_to_the_added_cartons_only(self): self.ask('美国1箱,每箱20KG,30x30x30cm,义乌美西普船海派报价') text=self.ask('再加1箱,每箱10KG,60x50x40cm') self.assertIn('40KG = 160元',text) self.assertNotIn('50KG =',text) def test_each_carton_minimum_remains_in_effect_after_addition(self): self.ask('美国2箱,每箱5KG,义乌美西普船海派报价') self.assertIn('36KG = 144元',self.ask('再加1箱,每箱5KG')) def test_declared_new_totals_are_validated_after_a_carton_change(self): self.ask('美国10箱每箱20KG,义乌美西普船海派报价') self.assertIn('240KG = 960元',self.ask('再加2箱每箱20KG,总重240KG')) self.assertIn('增减箱数推算的总重',self.ask('再加1箱每箱20KG,总重300KG')) self.assertNotIn('KG =',self.ask('换成华南')) self.assertIn('300KG',self.ask('总重300KG,共13箱')) def test_heterogeneous_carton_removal_requires_the_updated_breakdown(self): self.ask('美国1箱10KG,1箱30KG,义乌美西普船海派报价') self.assertIn('不同箱规',self.ask('减少1箱')) self.assertNotIn('KG =',self.ask('具体多少钱')) self.assertIn('30KG = 120元',self.ask('现在1箱30KG')) def test_rejected_services_remain_excluded_until_explicitly_selected_again(self): self.ask('美国1000KG义乌美东海运最便宜报价') text=self.ask('不走普船了,改成美森正班') self.assertIn('1000KG = 10000元',text) self.assertNotIn('普船海派',text) text=self.ask('不要美森,走普船') self.assertIn('1000KG = 6000元',text) self.assertNotIn('美森',text) self.ask('不走普船') text=self.ask('改成华南') self.assertNotIn('普船海派',text) self.assertNotIn('美森',text) self.assertIn('合德海派',text) self.assertIn('普船海派',self.ask('那还是普船吧')) self.assertIn('美森正班海派',self.ask('全部渠道报价')) def test_tax_corrections_and_pending_conflicts_are_not_reversed(self): self.ask('美国1000KG义乌美东海派报价') self.assertIn('1000KG = 6000元',self.ask('不要不包税,我要包税')) self.assertIn('没有匹配',self.ask('不是包税,是不包税')) self.assertIn('1000KG = 6000元',self.ask('那还是包税吧')) self.assertIn('1000KG = 6000元',self.ask('不是包税吗?')) self.assertIn('明确选择包税',self.ask('不要包税,也不要不包税')) self.assertNotIn('KG =',self.ask('换成华南')) self.assertIn('1000KG = 6000元',self.ask('要包税')) def test_new_batch_with_rejections_resets_old_shipping_conditions(self): self.ask('美国1000KG义乌美东普船海派报价') text=self.ask('新的一批美国30KG海派报价,不走普船') self.assertIn('美国 30KG',text) self.assertIn('华南',text) self.assertIn('美西',text) self.assertNotIn('普船海派',text) self.assertNotIn('1000KG',text) def test_calendar_questions_preserve_route_and_request_the_missing_dates(self): self.ask('美国1000KG义乌美东普船海派报价') for question in ('什么时候能到','大概哪天能收到'): text=self.ask(question) self.assertIn('25-30天左右提取',text) self.assertIn('预计开船日期',text) text=self.ask('快一点呢') self.assertIn('美森正班海派',text) self.assertNotIn('普船海派',text) text=self.ask('那比慢的贵多少') self.assertIn('运费差额为4000元',text) def test_ambiguous_plan_reference_keeps_context_for_a_named_selection(self): self.ask('美国1000KG海运最快和最慢报价') self.assertIn('1000KG',self.ask('这个多少钱')) self.assertIn('请指定方案名称',self.ask('第二个方案呢')) self.assertIn('1000KG = 4000元',self.ask('便宜的那个')) self.assertIn('1000KG = 4000元',self.ask('一共呢')) def test_tied_speed_costs_produce_a_range_without_mixing_tax_or_units(self): from logistics_chat.quote_comparison import speed_difference_lines from logistics_chat.quote_data import load_book from logistics_chat.quote_intent import parse_intent base={'origin':'义乌','zone':'美东','kind':'parcel','unit':'kg','tax':'included'} rates=[{**base,'service':name,'price':price,'eta':eta} for name,price,eta in ( ('快线A','8','12-15天'),('快线B','10','12-15天'), ('慢线A','5','30-35天'),('慢线B','6','30-35天'))] rates.append({**base,'service':'自税线','tax':'excluded','price':'999','eta':'20天'}) rates.append({**base,'service':'按方线','unit':'cbm','price':'999','eta':'20天'}) with self.app.app_context(): book=load_book() intent=parse_intent('美国1000KG义乌美东海派报价',set()) lines=speed_difference_lines(intent,book,rates,None) self.assertTrue(any(' · 包税 · ' in line and '2000至5000元' in line for line in lines),lines) self.assertTrue(any('不包税' in line and '差额为0元' in line for line in lines),lines) self.assertEqual(len(lines),2) self.assertEqual(speed_difference_lines(intent,book,[{**rates[0],'eta':'未提供时效'}],None),[]) def test_polite_relative_edits_and_unrecognized_adjustments_do_not_replace_the_total(self): self.ask('美国1000KG义乌美东普船海派报价') self.assertIn('1100KG = 6600元',self.ask('那我再加一百公斤')) self.assertIn('900KG = 5400元',self.ask('请帮我减少200KG')) self.assertIn('调整后的总重',self.ask('这批货后面增加100KG')) self.assertNotIn('KG =',self.ask('具体多少钱')) def test_difference_uses_the_displayed_two_decimal_costs(self): from logistics_chat.quote_comparison import speed_difference_lines from logistics_chat.quote_data import load_book from logistics_chat.quote_intent import parse_intent base={'origin':'义乌','zone':'美东','kind':'parcel','unit':'kg','tax':'included'} rates=[{**base,'service':'快线','price':'6.4','eta':'12天'}, {**base,'service':'慢线','price':'4.3','eta':'30天'}] with self.app.app_context(): intent=parse_intent('美国12.555KG义乌美东海派报价',set()) text='\n'.join(speed_difference_lines(intent,load_book(),rates,None)) self.assertIn('差额为26.36元',text) def test_difference_shows_both_speeds_for_each_origin_used_in_the_calculation(self): with self.app.app_context(): c=db.connect() c.execute("UPDATE quote_rates SET eta='船开后16天左右' WHERE service='美森正班海派' AND origin='华南'") c.commit();c.close() text=self.ask('美国1000KG美东海派最快最慢差价') self.assertIn('美森正班海派 · 义乌',text) self.assertIn('美森正班海派 · 华南',text) self.assertEqual(text.count('运费差额为4000元'),2) def test_volume_increments_keep_the_confirmed_total_and_units(self): self.ask('美国2CBM义乌美东普船海派报价') text=self.ask('再加1方') self.assertIn('总体积为3CBM',text) self.assertIn('3000元',text) text=self.ask('总体积减少0.5CBM') self.assertIn('总体积为2.5CBM',text) self.assertIn('2500元',text) def test_carton_changes_with_an_aggregate_volume_wait_for_the_updated_volume(self): self.ask('美国2箱每箱20KG,1CBM,义乌美东普船海派报价') self.assertIn('调整后的总体积',self.ask('再加1箱每箱20KG')) text=self.ask('总重60KG') self.assertIn('调整后的总体积',text) self.assertNotIn('KG =',text) text=self.ask('总体积1.5CBM') self.assertIn('1500元',text) def test_unrecognized_volume_increment_is_clarified_without_using_it_as_a_new_total(self): self.ask('美国2CBM义乌美东普船海派报价') text=self.ask('这批货后面增加1方') self.assertIn('调整后的总体积',text) self.assertNotIn('元/KG',text) self.assertIn('3000元',self.ask('总体积3CBM'))