How to use TestObfuscate method of text Package

Best Testkube code snippet using text.TestObfuscate

symmecrypt_test.go

Source:symmecrypt_test.go Github

copy

Full Screen

1package symmecrypt_test2import (3 "bytes"4 "encoding/json"5 "fmt"6 "io"7 "os"8 "reflect"9 "strings"10 "testing"11 "github.com/ovh/configstore"12 toml "github.com/pelletier/go-toml"13 "github.com/stretchr/testify/assert"14 "github.com/stretchr/testify/require"15 yaml "gopkg.in/yaml.v2"16 "github.com/ovh/symmecrypt"17 "github.com/ovh/symmecrypt/keyloader"18 "github.com/ovh/symmecrypt/stream"19)20func ProviderTest() (configstore.ItemList, error) {21 ret := configstore.ItemList{22 Items: []configstore.Item{23 configstore.NewItem(24 keyloader.EncryptionKeyConfigName,25 `{"key":"5fdb8af280b007a46553dfddb3f42bc10619dcabca8d4fdf5239b09445ab1a41","identifier":"test","sealed":false,"timestamp":1522325806,"cipher":"aes-gcm"}`,26 1,27 ),28 configstore.NewItem(29 keyloader.EncryptionKeyConfigName,30 `{"key":"7db2b4b695e11563edca94b0f9c7ad16919fc11eac414c1b1706cbaa3c3e61a4b884301ae4e8fbedcc4f000b9c52904f13ea9456379d373524dea7fef79b39f7","identifier":"test-composite","sealed":false,"timestamp":1522325758,"cipher":"aes-pmac-siv"}`,31 1,32 ),33 configstore.NewItem(34 keyloader.EncryptionKeyConfigName,35 `{"key":"QXdDW4N/jmJzpMu7i1zu4YF1opTn7H+eOk9CLFGBSFg=","identifier":"test-composite","sealed":false,"timestamp":1522325802,"cipher":"xchacha20-poly1305"}`,36 1,37 ),38 },39 }40 return ret, nil41}42// Bad config: conflicting timestamps43func ProviderTestKOTimestamp() (configstore.ItemList, error) {44 ret := configstore.ItemList{45 Items: []configstore.Item{46 configstore.NewItem(47 keyloader.EncryptionKeyConfigName,48 `{"key":"5fdb8af280b007a46553dfddb3f42bc10619dcabca8d4fdf5239b09445ab1a41","identifier":"test","sealed":false,"timestamp":1,"cipher":"aes-gcm"}`,49 1,50 ),51 configstore.NewItem(52 keyloader.EncryptionKeyConfigName,53 `{"key":"QXdDW4N/jmJzpMu7i1zu4YF1opTn7H+eOk9CLFGBSFg=","identifier":"test","sealed":false,"timestamp":1,"cipher":"xchacha20-poly1305"}`,54 1,55 ),56 },57 }58 return ret, nil59}60// Bad config: latest key non sealed61func ProviderTestKOSeal() (configstore.ItemList, error) {62 ret := configstore.ItemList{63 Items: []configstore.Item{64 configstore.NewItem(65 keyloader.EncryptionKeyConfigName,66 `{"key":"5fdb8af280b007a46553dfddb3f42bc10619dcabca8d4fdf5239b09445ab1a41","identifier":"test","sealed":false,"timestamp":10,"cipher":"aes-gcm"}`,67 1,68 ),69 configstore.NewItem(70 keyloader.EncryptionKeyConfigName,71 `{"key":"QXdDW4N/jmJzpMu7i1zu4YF1opTn7H+eOk9CLFGBSFg=","identifier":"test","sealed":true,"timestamp":1,"cipher":"xchacha20-poly1305"}`,72 1,73 ),74 },75 }76 return ret, nil77}78var KOTests = map[string]func() (configstore.ItemList, error){79 "timestamp": ProviderTestKOTimestamp,80 "seal": ProviderTestKOSeal,81}82func TestMain(m *testing.M) {83 configstore.RegisterProvider("test", ProviderTest)84 os.Exit(m.Run())85}86func TestEncryptDecrypt(t *testing.T) {87 text := []byte("eoeodecrytp")88 extra := []byte("aa")89 extra2 := []byte("bb")90 k, err := keyloader.LoadKey("test")91 if err != nil {92 t.Fatal(err)93 }94 encr, err := k.Encrypt(text)95 if err != nil {96 t.Fatal(err)97 }98 encrExtra, err := k.Encrypt(text, extra, extra2)99 if err != nil {100 t.Fatal(err)101 }102 decr, err := k.Decrypt(encr)103 if err != nil {104 t.Fatal(err)105 }106 decrExtra, err := k.Decrypt(encrExtra, extra, extra2)107 if err != nil {108 t.Fatal(err)109 }110 _, err = k.Decrypt(encrExtra)111 if err == nil {112 t.Fatal("successfully decrypted cipher+extra without using extra -> ERROR")113 }114 _, err = k.Decrypt(encrExtra, []byte("cc"), []byte("dd"))115 if err == nil {116 t.Fatal("successfully decrypted cipher+extra using wrong extra -> ERROR")117 }118 _, err = k.Decrypt(encr, extra, extra2)119 if err == nil {120 t.Fatal("succerssfully decrypted cipher while using extra data -> ERROR")121 }122 if string(decr) != string(text) {123 t.Errorf("not equal when decrypt text encrypted, %s != %s", text, decr)124 }125 if string(decrExtra) != string(text) {126 t.Errorf("not equal when decrypt text encrypted [extra data], %s != %s", text, decrExtra)127 }128}129type testObfuscate struct {130 Name string131 Amount int132 InterfaceNumber interface{}133}134func TestEncryptDecryptMarshal(t *testing.T) {135 k, err := keyloader.LoadKey("test")136 if err != nil {137 t.Fatal(err)138 }139 origin := &testObfuscate{140 Name: "test",141 Amount: 10,142 InterfaceNumber: 2345678954,143 }144 extra := []byte("aa")145 extra2 := []byte("bb")146 r, err := k.EncryptMarshal(origin)147 if err != nil {148 t.Fatal(err)149 }150 rExtra, err := k.EncryptMarshal(origin, extra, extra2)151 if err != nil {152 t.Fatal(err)153 }154 target := &testObfuscate{}155 targetExtra := &testObfuscate{}156 err = k.DecryptMarshal(r, target)157 if err != nil {158 t.Fatal(err)159 }160 err = k.DecryptMarshal(rExtra, targetExtra, extra, extra2)161 if err != nil {162 t.Fatal(err)163 }164 err = k.DecryptMarshal(rExtra, targetExtra)165 if err == nil {166 t.Fatal("succerssfully decrypted cipher without using extra data -> ERROR")167 }168 if target.Name != origin.Name || target.Amount != origin.Amount || fmt.Sprint(origin.InterfaceNumber) != fmt.Sprint(target.InterfaceNumber) {169 t.Errorf("Not same deobfuscated result %s, %d, %v", target.Name, target.Amount, target.InterfaceNumber)170 }171 if targetExtra.Name != origin.Name || targetExtra.Amount != origin.Amount || fmt.Sprint(origin.InterfaceNumber) != fmt.Sprint(targetExtra.InterfaceNumber) {172 t.Errorf("Not same deobfuscated result %s, %d, %v", targetExtra.Name, targetExtra.Amount, targetExtra.InterfaceNumber)173 }174}175func TestCompositeKey(t *testing.T) {176 kC, err := keyloader.LoadKey("test-composite")177 if err != nil {178 t.Fatal(err)179 }180 var k, k2 symmecrypt.Key181 comp, ok := kC.(symmecrypt.CompositeKey)182 if !ok {183 t.Fatal("Expected a composite key instance")184 }185 if len(comp) < 2 {186 t.Fatalf("composite len should be 2, got %d", len(comp))187 }188 k = comp[0]189 k2 = comp[1]190 text := []byte("eoeodecrytp")191 encr, err := kC.Encrypt(text)192 if err != nil {193 t.Fatal(err)194 }195 decr, err := kC.Decrypt(encr)196 if err != nil {197 t.Fatal(err)198 }199 if string(text) != string(decr) {200 t.Errorf("not equal when decrypt text encrypted, %s != %s", text, decr)201 }202 decr1, err := k.Decrypt(encr)203 if err != nil {204 t.Fatal(err)205 }206 if string(text) != string(decr1) {207 t.Errorf("not equal when decrypt text encrypted, %s != %s", text, decr1)208 }209 _, err = k2.Decrypt(encr)210 if err == nil {211 t.Fatal("successfully decrypted composite encrypt result with low-priority key -> ERROR")212 }213 encr2, err := k2.Encrypt(text)214 if err != nil {215 t.Fatal(err)216 }217 decr2, err := kC.Decrypt(encr2)218 if err != nil {219 t.Fatal(err)220 }221 if string(text) != string(decr2) {222 t.Errorf("not equal when decrypt text encrypted, %s != %s", text, decr2)223 }224 extra := []byte("aa")225 extra2 := []byte("bb")226 encr3, err := k.Encrypt(text, extra, extra2)227 if err != nil {228 t.Fatal(err)229 }230 decr3, err := kC.Decrypt(encr3, extra, extra2)231 if err != nil {232 t.Fatal(err)233 }234 if string(text) != string(decr3) {235 t.Errorf("not equal when decrypt text encrypted, %s != %s", text, decr3)236 }237 _, err = kC.Decrypt(encr3)238 if err == nil {239 t.Fatal("successfully decrypted cipher+extra without using extra -> ERROR")240 }241}242// TestWriterWithEncoders is about testing symmecrypt directly coupled with json, yaml and toml encoder/decoder243func TestWriterWithEncoders(t *testing.T) {244 // Load a global key245 k, err := keyloader.LoadKey("test")246 if err != nil {247 t.Fatal(err)248 }249 // Define common interfaces to json, yaml, toml encoders and decoders250 type encoder interface {251 Encode(v interface{}) error252 }253 type decoder interface {254 Decode(v interface{}) error255 }256 // Define testscases257 var testcases = []struct {258 data interface{}259 k symmecrypt.Key260 extras [][]byte261 encoder func(io.Writer) encoder262 decoder func(io.Reader) decoder263 }{264 {265 data: struct {266 A string267 B int268 C bool269 }{A: "A", B: 1, C: true},270 k: k,271 extras: [][]byte{[]byte("aa"), []byte("bb")},272 encoder: func(w io.Writer) encoder {273 return yaml.NewEncoder(w)274 },275 decoder: func(r io.Reader) decoder {276 return yaml.NewDecoder(r)277 },278 }, {279 data: struct {280 A string281 B int282 C bool283 }{A: "A", B: 1, C: true},284 k: k,285 extras: [][]byte{[]byte("aa"), []byte("bb")},286 encoder: func(w io.Writer) encoder {287 return json.NewEncoder(w)288 },289 decoder: func(r io.Reader) decoder {290 return json.NewDecoder(r)291 },292 }, {293 data: struct {294 A string295 B int296 C bool297 }{A: "AA", B: 11, C: false},298 k: k,299 extras: [][]byte{[]byte("aa"), []byte("bb")},300 encoder: func(w io.Writer) encoder {301 return toml.NewEncoder(w)302 },303 decoder: func(r io.Reader) decoder {304 return toml.NewDecoder(r)305 },306 },307 }308 // Run the testcases309 for _, tt := range testcases {310 var writeBuf bytes.Buffer311 // Instantiate a writer and an encoder312 w := symmecrypt.NewWriter(&writeBuf, tt.k, tt.extras...)313 enc := tt.encoder(w)314 // Encode315 if err := enc.Encode(tt.data); err != nil {316 t.Fatal(err)317 }318 // Close (flush)319 if err := w.Close(); err != nil {320 t.Fatal(err)321 }322 // Instantiate a reader and an decoder323 r, err := symmecrypt.NewReader(&writeBuf, k, tt.extras...)324 if err != nil {325 t.Fatal(err)326 }327 dec := tt.decoder(r)328 var actual struct {329 A string330 B int331 C bool332 }333 // Decode334 if err := dec.Decode(&actual); err != nil {335 t.Fatal(err)336 }337 // Check338 if !reflect.DeepEqual(tt.data, actual) {339 t.Fatalf("expected: %+v but got %+v", tt.data, actual)340 }341 }342}343func TestKeyloaderKO(t *testing.T) {344 for testName, provider := range KOTests {345 st := configstore.NewStore()346 st.RegisterProvider("test", provider)347 _, err := keyloader.LoadKeyFromStore("test", st)348 if err == nil {349 t.Fatalf("nil error with KO config (%s)", testName)350 }351 }352}353func ExampleNewWriter() {354 k, err := keyloader.LoadKey("test")355 if err != nil {356 panic(err)357 }358 w := symmecrypt.NewWriter(os.Stdout, k)359 _, err = w.Write([]byte("secret content"))360 if err != nil {361 panic(err)362 }363 err = w.Close()364 if err != nil {365 panic(err)366 }367}368func ExampleNewReader() {369 k, err := keyloader.LoadKey("test")370 if err != nil {371 panic(err)372 }373 encryptedContent, err := k.Encrypt([]byte("secret content"))374 if err != nil {375 panic(err)376 }377 src := bytes.NewReader(encryptedContent)378 reader, err := symmecrypt.NewReader(src, k)379 if err != nil {380 panic(err)381 }382 _, err = io.Copy(os.Stdout, reader)383 if err != nil {384 panic(err)385 }386}387func TestWriteAndRead(t *testing.T) {388 var content = "this is a very sensitive content"389 k, err := keyloader.LoadKey("test")390 require.NoError(t, err)391 var buf bytes.Buffer392 w := symmecrypt.NewWriter(&buf, k)393 _, err = io.Copy(w, strings.NewReader(content))394 require.NoError(t, err)395 require.NoError(t, w.Close())396 r, err := symmecrypt.NewReader(bytes.NewReader(buf.Bytes()), k)397 require.NoError(t, err)398 var out bytes.Buffer399 _, err = io.Copy(&out, r)400 require.NoError(t, err)401 assert.Equal(t, content, out.String())402}403func TestChunksWriterAndChunksReader(t *testing.T) {404 var chunckSize = 11405 var content = "this is a very sensitive content"406 k, err := keyloader.LoadKey("test")407 require.NoError(t, err)408 var encryptedWriter bytes.Buffer409 cw := stream.NewWriter(&encryptedWriter, k, chunckSize)410 n, err := io.Copy(cw, strings.NewReader(content))411 require.NoError(t, err)412 err = cw.Close()413 require.NoError(t, err)414 encryptedContent := encryptedWriter.Bytes()415 t.Logf("%d bytes encrypted: %x", n, encryptedContent)416 decryptedOutput := bytes.Buffer{}417 cr := stream.NewReader(bytes.NewReader(encryptedContent), k, chunckSize)418 n, err = io.Copy(&decryptedOutput, cr)419 require.NoError(t, err)420 result := decryptedOutput.String()421 t.Logf("%d bytes decrypted: %s", n, result)422 assert.Equal(t, content, result)423}424func BenchmarkChunksWriter(b *testing.B) {425 var chunckSize = 10426 var content = "this is a very sensitive content"427 for n := 0; n < b.N; n++ {428 k, err := keyloader.LoadKey("test")429 require.NoError(b, err)430 var encryptedWriter bytes.Buffer431 cw := stream.NewWriter(&encryptedWriter, k, chunckSize)432 _, err = io.Copy(cw, strings.NewReader(content))433 require.NoError(b, err)434 err = cw.Close()435 require.NoError(b, err)436 }437}438func BenchmarkChunksReader(b *testing.B) {439 var chunckSize = 10440 var content = "this is a very sensitive content"441 k, err := keyloader.LoadKey("test")442 require.NoError(b, err)443 var encryptedWriter bytes.Buffer444 cw := stream.NewWriter(&encryptedWriter, k, chunckSize)445 n, err := io.Copy(cw, strings.NewReader(content))446 require.NoError(b, err)447 err = cw.Close()448 require.NoError(b, err)449 encryptedContent := encryptedWriter.Bytes()450 b.Logf("%d bytes encrypted: %x", n, encryptedContent)451 for n := 0; n < b.N; n++ {452 decryptedOutput := bytes.Buffer{}453 cr := stream.NewReader(bytes.NewReader(encryptedContent), k, chunckSize)454 _, err := io.Copy(&decryptedOutput, cr)455 require.NoError(b, err)456 result := decryptedOutput.String()457 assert.Equal(b, content, result)458 }459}...

Full Screen

Full Screen

obfuscate_test.go

Source:obfuscate_test.go Github

copy

Full Screen

2import (3 "testing"4 "github.com/stretchr/testify/assert"5)6func TestObfuscate(t *testing.T) {7 t.Run("test Obfuscate standard string, 2 chars from begining and end", func(t *testing.T) {8 in := "Some Long Token !@31209301293"9 out := ObfuscateLR(in, 2, 2)10 assert.Equal(t, "So*************************93", out)11 })12 t.Run("test Obfuscate short string too much chars left", func(t *testing.T) {13 in := "Short"14 out := ObfuscateLR(in, 20, 0)15 assert.Equal(t, "*****", out)16 })17 t.Run("test Obfuscate short string too much right", func(t *testing.T) {18 in := "Short"19 out := ObfuscateLR(in, 0, 20)20 assert.Equal(t, "*****", out)...

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Hello, playground")4 fmt.Println(text.TestObfuscate("Hello, playground"))5}6import (7func main() {8 fmt.Println("Hello, playground")9 fmt.Println(text.TestObfuscate("Hello, playground"))10}11import (12func main() {13 fmt.Println("Hello, playground")14 fmt.Println(text.TestObfuscate("Hello, playground"))15}16import (17func main() {18 fmt.Println("Hello, playground")19 fmt.Println(text.TestObfuscate("Hello, playground"))20}21import (22func main() {23 fmt.Println("Hello, playground")24 fmt.Println(text.TestObfuscate("Hello, playground"))25}26import (27func main() {28 fmt.Println("Hello, playground")29 fmt.Println(text.TestObfuscate("Hello, playground"))30}31import (32func main() {33 fmt.Println("Hello, playground")34 fmt.Println(text.TestObfuscate("Hello, playground"))35}36import (37func main() {38 fmt.Println("Hello, playground")39 fmt.Println(text.TestObfuscate("Hello, playground"))40}41import (

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Testing obfuscate")4 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 0, 5))5 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 0, 1))6 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 1, 1))7 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 0, 3))8 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 1, 3))9 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 2, 3))10 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 3, 3))11 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 4, 3))12 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 5, 3))13 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 6, 3))14 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 7, 3))15 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 8, 3))16 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 9, 3))17 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 10, 3))18 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 11, 3))19 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 12, 3))20 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 13, 3))21 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 14, 3))22 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 15, 3))23 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 16, 3))24 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 17, 3))25 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 18, 3))26 fmt.Println(obfuscator.Text{}.TestObfuscate("hello", 19, 3))27 fmt.Println(obfusc

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Hello, playground")4 fmt.Println(text.TestObfuscate("Krishna"))5}6import "fmt"7func TestObfuscate(s string) string {8 fmt.Println("Hello, playground")9}10import "fmt"11func TestObfuscate(s string) string {12 fmt.Println("Hello, playground")13}14import (15func main() {16 fmt.Println("Hello, playground")17 fmt.Println(text.TestObfuscate("Krishna"))18}

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println(text.TestObfuscate("Hello World"))4}5import (6func Obfuscate(s string) string {7 b.Grow(len(s))8 for _, c := range s {9 if c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' {10 b.WriteRune('*')11 } else {12 b.WriteRune(c)13 }14 }15 return b.String()16}17import (18func TestObfuscate(t *testing.T) {19 if Obfuscate("Hello World") != "H*ll* W*rld" {20 t.Error("Expected H*ll* W*rld")21 }22}23--- PASS: TestObfuscate (0.00s)24import (25func Obfuscate(s string) string {26 b.Grow(len(s))27 for _, c := range s {28 if c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' {29 b.WriteRune('*')30 } else {31 b.WriteRune(c)

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 text := new(obfuscation.Text)4 text.Set("the quick brown fox jumped over the lazy dog")5 text.TestObfuscate()6 fmt.Println(text.Get())7}8import (9func main() {10 text := new(obfuscation.Text)11 text.Set("the quick brown fox jumped over the lazy dog")12 text.TestObfuscate()13 text.TestDeobfuscate()14 fmt.Println(text.Get())15}16import (17func main() {18 text := new(obfuscation.Text)19 text.Set("the quick brown fox jumped over the lazy dog")20 text.TestObfuscate()21 text.TestDeobfuscate()22 fmt.Println(text.Get())23 fmt.Println(text.TestGetChar(5))24}25import (26func main() {27 text := new(obfuscation.Text)28 text.Set("the quick brown fox jumped over the lazy dog")29 text.TestObfuscate()30 text.TestDeobfuscate()

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Hello World!")4 fmt.Println(text.TestObfuscate("Hello World!"))5}6import (7func main() {8 fmt.Println("Hello World!")9 fmt.Println(text.TestObfuscate("Hello World!"))10}11import (12func main() {13 fmt.Println("Hello World!")14 fmt.Println(text.TestObfuscate("Hello World!"))15}16import (17func main() {18 fmt.Println("Hello World!")19 fmt.Println(text.TestObfuscate("Hello World!"))20}

Full Screen

Full Screen

TestObfuscate

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Hello World!")4 txt := text.Text{"Hello World!"}5 fmt.Println(txt.TestObfuscate())6}7import (8func main() {9 fmt.Println("Enter the text to obfuscate:")10 reader := bufio.NewReader(os.Stdin)11 input, _ := reader.ReadString('12 txt := text.Text{input}13 fmt.Println(txt.Obfuscate())14}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Testkube automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful